(水)垒色子 蓝桥杯2015A组题

分析:一眼便能看出这是道DP题(你若是问我怎么看出来的,那就是那种牵一发而动全身的感觉,其中任意一个色子的变化都会引起全局变化),至于状态的话取得越细越好,就能尽量减少决策的种类,更易于状态的转移,所以我们在以dp[i]表示前i个的(以后指的前i个都是指位于下部的)种类的基础上再加上一维,即还要将第i个的某种状态表示出来,因为只要第i个朝下的数字确定,那么第i-1个不能朝下的数字就能确定了(这里我们可以在输入时进行记录)。

综上思想,我们以dp[i][j]表示前i个且第i个为j朝下的种类数(其实也可将j的确定作为一种决策,即以dp[i]表示前i的种类,但这样的话转移起来更加啰嗦,所以还是那句话,状态分的越细越好(当然这只是我的一面之词)那么状态转移方程也就很容易写出来了即dp[i][j]=Σ(1=<k<=6且k为满足条件的)4*dp[i-1][k](这里的k满足的条件为,k的对面与j不互斥),这里我们可以用一个数组记录k是否满足条件。

这道题还有一个值得注意的地方,也就是i的上限,因为n最大有10的9次幂,所以开一个这么大的二维数组是不可能的,由于i总是由i-1所推出来的,且最终也只要利用到i的结果,所以我们可以开一个二维为2的二维数组来循环利用

只需要i%2来表示二维就行了。其实很多问题我们可以用一维来当二维使用,进一步优化空间复杂度,这个题是不行的,具体怎么时候可以使用的话过几天我会出一篇文章来给大家学习学习的。

解释得也差不多了,下面上代码:

#include<cstdio>
#define Y 1000000007
int dp[2][10];
bool vis[7][7];
int ans;
int n, m;
int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		int aa, bb;
		scanf("%d%d", &aa, &bb);
		vis[aa][bb - 3 < 1 ? bb + 3 : bb - 3] = 1;           //如果aa与bb互斥,那么当aa在上面时,下面不能为bb,则下面的色子朝下的面不能为bb的对家
		vis[bb][aa - 3 < 1 ? aa + 3 : aa - 3] = 1;	     //同理如果aa与bb互斥,那么当bb在上面时,..........aa............................aa......
	}
	for (int i = 1; i <= 6; i++)
		dp[1][i] = 4;
	for (int i = 2; i <= n; i++)
		for (int j = 1; j <= 6; j++)
		{
			for (int k = 1; k <= 6; k++)
				if (!vis[j][k]) dp[i % 2][j] = (dp[i % 2][j] + 4 * dp[(i - 1) % 2][k]) % Y;
		}
	for (int i = 1; i <= 6; i++)
		ans = (ans + dp[n % 2][i]) % Y;
	printf("%d\n", ans);
	return 0;
}


你可能感兴趣的:(dp,ACM,垒色子,题解报告)