【xdoj难题集】1203 Happy to Eliminate

又沉浸在了模运算的海洋中了。。。这个模运算真的太恶心了,有时候又神奇又复杂,还是经验不够丰富,导致了很多错误。在此我也得到一个教训,对于涉及模运算的问题,宁可写的复杂一些,难看一些,也不要耍小聪明,那样一旦失误造成的后果可能是惨重的。

说说思路,一眼看上去就知道应该是状态压缩dp,但是这个复杂度得好好算一算,首先两数之积不大于40,说明最小边不大于6,因为是三层所以要储存两层内容,但是这样一算最大复杂度就是4的12次方,这个是不可接受的,后来想了想发现可以优化一下,我们其实不需要知道最上面的是什么,只需要知道他是不是和下面的颜色相同即可,因为之前的dp肯定会排除横着三个相邻的情况,所以是成立的(当然我当时心里也不是百分百的把握,但想想也只能这么做了,就做下去了),这样复杂度就变成了2的18次方了,再加上一些优化是可以完成的,我们首先要知道初始状况,所以第一行直接进行一个搜索,把可以的情况都找出来(这里参考了聚聚的题解,万分感谢,不过大体思路还是自己研究的,因为我根本看不懂那些位运算。。。),然后再看时查找,最后把所有情况相加即可。

这道题其实实现并不难,但是被位运算坑了好久,最后终于把问题都排查出来了,还是那句话,宁可难看,但要正确,这个教训真的记住了。另外我这个代码速度还是可以的,好像暂时是第一,我觉得有这么几个优化,首先如果只有一种颜色而且边最大值大于等于3那么直接回0,另外一些发现没用的情况直接pass,还有一点,就是取模运算听说挺费时间的,所以像这种只有加法的情况可以用判断减法代替取模。

代码

# include <stdio.h>
# include <string.h>
# include <algorithm>

using namespace std;

const int Mod = 1e9 + 7;

const int MAX_N = 1 << 18;

int dp[2][MAX_N];
int N, M, K, cnt;

void dfs(int h , int num , int a , int b)
{
	if(h == M)
	{
		dp[0][num] = 1;

		return;
	}
	
	num <<= 3;
	int i;
	for(i = 0 ; i < K ; i++)
	{
		if(i == a && i == b)
			continue;
		dfs(h + 1 , num | i, b , i);
	}
}

int main()
{
	while(~scanf("%d %d %d", &N, &M, &K))
	{
		if(K == 1 && (M > 2 || N > 2))
		{
			puts("0");
			continue;
		}
		
		if(M > N)
			swap(N , M);
		
		memset(dp , 0 , sizeof(dp));
		int * now = dp[0], * next = dp[1];
		dfs(0 , 0 , -1 , -1);
		
		int q = 1 << (3 * M);
		//int num = 0;
		int i, j, k, g;
		for(i = 1 ; i < N ; i++)
		{
			for(j = 0 ; j < M ; j++)
			{
				fill(next , next + q , 0);

				for(k = 0 ; k < q ; k++)
				{
					if(!now[k])
						continue;

					for(g = 0 ; g < K ; g++)
					{
						if(j > 1 && (k & (3 << (3 * (j - 1)))) == (g << (3 * (j - 1))) && (k & (3 << (3 * (j - 2)))) == (g << (3 * (j - 2))))
							continue;                    
						
						int op = ((k >> (3 * j)) & 7), ch;
						if((op & 3) == g && op > 3)
							continue;

						if((op & 3) == g)
						{
							ch = k & ~(3 << (3 * j)) | ((4 + g) << (3 * j));
						}
						else
							ch = k & ~(7 << (3 * j)) | (g << (3 * j));

						
						next[ch] += now[k];
						if(next[ch] > Mod)
							next[ch] -= Mod;
					}
				}
				swap(now , next);
				
			}
		}
		
		int ans = 0, w;
		for(w = 0 ; w < q ; w++)
		{
			ans += now[w];
			if(ans > Mod)
				ans -= Mod;
		}
		
		printf("%d\n", ans);
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40772738/article/details/79936245
今日推荐