【蓝桥杯】 历届试题 格子刷油漆(动态规划)—— 酱懵静

历届试题 格子刷油漆

问题描述
在一个长为n(待输入的一个正整数<=1000),高为2的格子里面对每个格子进行刷漆
规则是在其相邻格子(包括对角相邻)间可以进行一步的位移,最后输出所有的刷漆方案
由于这里n的单位增长会使得结果猛然增加,因此最后的结果要求对1000000007进行取余(十亿零七)

样例输入:2 样例输出:24
样例输入:3 样例输出:96
样例输入:22 样例输出:359635897



---分割线---



分析:
对于本题模型:在已知几个基本行走方式的前提下去推算行走路线数量
显然这里面是存在着很强的递推关系的,即每一个行走路线的确定是会和前面的有关
因此这里需要去寻找动态转移方程
而寻找动态转移方程,那就又回到了这个模型的本身,接下来我们来推理这个过程:
对于刷漆而言,你的出发点在总体上分为两大类:

  1. 从四个顶点之一出发
  2. 从中间出发
(一)首先我们来分析一下从顶点出发的模型
情况①,第一步走同一列的另一个格子,然后走下一列,接着重复这个过程,如下图:

在这里插入图片描述
即A->B,接下来B就可以到C或者D。假设到了C,则此后C->D,然后D又可以到E或者F,这样反复执行这个过程,直到最后走到最后一列为止(即走完了所有的格子)。
这种情况(一趟过去)我们用数组a来表示,则可以把问题规模由a[i]转换成a[i-1]
于是得到状态转移方程:a[i]=2 ╳ a[i-1]

情况②,第一步走另一列,然后又走下一列,然后……当走到最后一列之后再走同一列的另一个,最后返回。返回时,由于格子的高度为2,那么之后返回时,其路径是唯一的,如下图:
在这里插入图片描述
即A->C或者A->D,之后的起点则为C或者D,假设为C,然后你可以(C->E或者C->F),反复执行上面这种往下一列延伸的方式,直到最后走到了最后一列,这时候你无路可走,为了走完所有格子你必须返回到B点。需要注意的是,由于高度为2,回退时,对于每一列而言都已经走过了其中某一格,那么其回退的路径即已经固定了(即回退过程唯一,不用再管)
这种情况用数组b来表示,则可以把问题规模由b[i]转换成b[i-1]
于是得到状态转移方程:b[i]=2 ╳ b[i-1]

情况③,第一步走另一列再到同一列的另一个格子再到另一列,如下图:
在这里插入图片描述
在这里插入图片描述
即A->C->B->D或者A->D->B->C,假设最后到了C,则C又可以到E或者F,然后由其中任意点再返回到D,最后再去其中的另一个点,如此反复执行这个过程,即是在不断地往后面一列推进,直到最后一列(这种情况下显然是有点同①相似,即一次性遍历完所有格子,也就不再需要像②那样返回)
于是这种不用返回的情况我们同样也用数组a来表示,就把问题规模转换成了a[i-2]
于是得到状态转移方程:a[i]=2 ╳ 2 ╳ a[i-2]
说明:第一个2乘的是(选C或者D),而第二个2乘的是(选E或者F)

综上便分析出了所有的基础遍历方式
于是得到转移方程:a[i] = 2 ╳ a[i-1] + b[i] + 2 ╳ 2 ╳ a[i-2]
而顶点有4个,于是最后的总的遍历方案sum=4 ╳ a[i]


(二)接下来分析从中间出发的方式(见下图)

在这里插入图片描述
假设我们从图中i=3(E点)处出发(以i为分割线,将图分为左边的ABCDEF以及右边的GHIJ),为了遍历所有格子,我们需要先走完左边的这个整体(特别注意:这里必须倒回到F才能继续走右边),然后再把右边视为以G或者H为起点的一组格子将其走完(因此这里需要乘以2,两种起点出发嘛)。
分析上述的流程,可以得到从中间出发的方案数为:( b[i] ) ╳ ( 2 ╳ a[n-i] )

同理,我们可以先走右边的EFGHIJ,然后再走左边的ABCD
这样的方案数为:(b[n-i+1]) ╳ (2 ╳ a[i-1])

由于上面的所有起始点都是以E为出发点行走的,我们同理也可以以F为起点出发,那么从第i列开始刷漆的方法就有:[ b[i] ╳ (2╳a[n-i]) + (b[n-i+1]) ╳ (2╳a[i-1]) ] ╳ 2

所以最终的方案数sum为:sum = 4╳a[n] + 2 ╳ 2 ╳ ( b[ i ] ╳ a[ n-i ] + b[ n-i+1 ] ╳ a[ i-1 ])
现在递推公式已然得出,最后的一步就是,初值的确定
根据上面的公式可以看出,在循环中i最低需要从3开始,因为a[0]或者b[0]无意义嘛
所以下面我们需要手动地给出i=1和2的情况:
a[1]=1; //显然只有一种情况
a[2]=6; //a[2]=2+2+2由(一)中的三种子情况分别得出
情况①:
在这里插入图片描述
在这里插入图片描述
情况②:
在这里插入图片描述
在这里插入图片描述
情况③:
在这里插入图片描述
在这里插入图片描述
b[2]=2; //显然这种情况下只能是两种,对应上图的情况②
b[1]=1; //b这种刷漆方式至少需要两个格子(即最低i=2),所以b[1]严格意义上说来是0,但是由于b[2]=2,且存在公式b[i]=2*b[i-1],故这里反推b[1]=1

下面给出本题的完整代码:


---分割线---


#include<iostream>
using namespace std;
const int MOD=1000000007;
int main()
{
	int n,i;
	cin>>n;
	if(n==1)
	{
		cout<<2<<endl;
		return 0;
	}
	long long a[1005],b[1005];
	a[1]=1,a[2]=6;
	b[1]=1,b[2]=2;
	for(i=3;i<=n;i++)				//一个顶点出出发 
	{
		b[i]=(2*b[i-1])%MOD;		//边操作边取余
		a[i]=(2*a[i-1]+b[i]+4*a[i-2])%MOD;
	}
	long long sum=4*a[n];			//4个顶点(特别注意这里的sum的数据类型需要用long long,否则这里是有可能在乘以4后数据溢出的)
	for(i=2;i<n;i++)				//从中间开始,注意i的范围是大于1小于n 
		sum=(sum+4*(b[i]*a[n-i]+b[n-i+1]*a[i-1]))%MOD;
	cout<<sum<<endl; 
	return 0;
 }

发布了30 篇原创文章 · 获赞 67 · 访问量 3054

猜你喜欢

转载自blog.csdn.net/the_ZED/article/details/100118930