[好题][dp]格子刷油漆 2013年蓝桥杯

题目描述

X国的一段古城墙的顶端可以看成  2*N个格子组成的矩形(如下图所示),现需要把这些格子刷上保护漆。
 


你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动到较远的格子(因为油漆未干不能踩!)
比如:a  d  b  c  e  f  就是合格的刷漆顺序。
c  e  f  d  a  b  是另一种合适的方案。
当已知  N  时,求总的方案数。当N较大时,结果会迅速增大,请把结果对  1000000007  (十亿零七)  取模。

输入

输入数据为一个正整数(不大于1000)

输出

输出数据为一个正整数。

样例输入

3 

样例输出

96

题意: 有一个2*n的矩形,每次可以往相邻格子移动,也可以对角移动,要求不能重复经过格子,求从任意格子出发的不同路径数。

分析: 设一下状态数组dp[i][0]和dp[i][1],dp[i][0]表示长度为i的格子,从左上角出发遍历全部格子然后返回起点上或下面点的方案数,dp[i][1]表示长度为i的格子,从左上角出发遍历全部格子然后到任意终点的方案数。对于dp[i][0]从图中可以看出从第i列到第i+1列共有两种方式,因此dp[i][0] = 2*dp[i-1][0]。

对于dp[i][1]的求解会比较复杂,首先可以按照第一步的不同把情况分为三类,如下图所示:

之后可以发现可以用前两列路线的不同情况来求出对应的路径数,这个过程需要自己画图多分析一下,这样最终得到dp[i][1] = 4*dp[i-2][1]+2*dp[i-1][0]+2*dp[i-1][1]。

在得到dp数组后就可以统计答案了,首先起点可能是四个角落,这样的路径数是4*dp[n][1],剩下的情况如下图:

剩下的起点就是在第2~n-1列上,对于确定的一列,起点可以在上面也可以在下面,所以最后路径数要乘2。再看一下起点在上面时的路径数,此时可以先往左走或者先往右走,不管哪种走法都需要先把单侧点遍历完然后到达起点下方点,之后再向另一侧走,这里有两种方向,所以要乘2。

综合一下最终的路径数就是4*dp[n][1]+sum(4*dp[i][0]*dp[n-i][1]+4*dp[n-i+1][0]*dp[i-1][1]),i∈[2, n-1]。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long
#define mod 1000000007
using namespace std;

int n, dp[1005][2]; 
//dp[i][0]表示长度为i的格子,从左上角出发遍历全部格子然后返回起点上或下面点的方案数
//dp[i][1]表示长度为i的格子,从左上角出发遍历全部格子然后到任意终点的方案数 

signed main()
{
	cin >> n;
	dp[1][0] = 1, dp[2][0] = 2;
	dp[1][1] = 1, dp[2][1] = 6;
	for(int i = 3; i <= n; i++)
	{
		dp[i][0] = dp[i-1][0]*2%mod;
		dp[i][1] = (4*dp[i-2][1]%mod+2*dp[i-1][1]%mod+2*dp[i-1][0]%mod)%mod;
	}
	int ans = 4*dp[n][1]%mod;//四个角落为起点的方案数 
	for(int i = 2; i <= n-1; i++)
		ans = (ans+4*dp[i][0]*dp[n-i][1]%mod+4*dp[n-i+1][0]*dp[i-1][1]%mod)%mod;
	cout << ans;
    return 0;
}

  

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值