FZ_2011 Carcassonne 状态DP

http://acm.hdu.edu.cn/showproblem.php?pid=4064

题意:给定n*m给方块四边形,四边形的每边都有一种颜色,可以为R、F、C中的一种,在只可以旋转单个方块的前提下,问总共有多少种旋转方法,可以使最终的结果变成每两个共享一条边的四边形颜色一样。

算法:状态DP, 状态为:dp[row][now_s] = ∑ dp[row-1][pre_s] , 其中pre_s 到now_s 为合法转移。

复杂度分析:n,m<=12 ,用dfs求now_s的每个值,因为每个方块有四种形态, 因此复杂度为 O(n) = 4^m * n ,直接求的话会得到一个TLE,这里需要优化,即在枚举每个方块的形态的时候,将方块的形态相同的归为一类,即可以通过乘以一个系数来避免重复计算,这样的优化可以将时间降低到500ms 。


代码:

#include<stdio.h>
#include<string.h>
#define MOD 1000000007

int n,m,all,row,now,pre;
char map[13][13][5];
long long dp[2][540000] ;

int get_val(char a)
{
	if(a == 'C')
		return 0 ; 
	if(a == 'F')
		return 1 ;
	if(a == 'R')	
		return 2 ;
}
bool ok(int row,int col,int i,int j)
{
	if(map[row][col][i] != map[row][col][j])	
		return false ;
	if(map[row][col][(i+1)%4] != map[row][col][(j+1)%4])
		return false ;
	if(map[row][col][(i+2)%4] != map[row][col][(j+2)%4])
		return false ;
	if(map[row][col][(i+3)%4] != map[row][col][(j+3)%4])
		return false ;	
	return true ;	
}	


//V: 横向前一个方块的右边的颜色情况; col : 当前的列  ; nnum:当前行的重叠状态数 
void dfs(int v,int col,int now_s, int pre_s,long long nnum)			
{
	
	if(col > m)
	{
		dp[now][now_s] = (dp[now][now_s] + dp[pre][pre_s]*nnum) % MOD ;
		return ;		
	}	
	
	bool vis[4] ;
	memset(vis,false,sizeof(vis));
	for(int i=0;i<4;i++)		//0:C 1:F 2:R
	{
		if(vis[i])	continue ;			//和之前的状态重合,已经计算过了 。 
		if(v==3)						//第一个方块,不受限制 
		{
			vis[i] = true ;
			int n_v = get_val(map[row][col][(i+1)%4]) ;
			int pre_a = get_val(map[row][col][i]);
			int now_a = get_val(map[row][col][(i+2)%4]);
			long long num = 1 ;
			for(int j=i+1;j<4;j++)			//寻找相同的状态。 
			{
				if(ok(row,col,i,j))
				{
					num++ ;
					vis[j] = true ;	
				}	
			}
			dfs(n_v,col+1,now_s*3+now_a,pre_s*3+pre_a,nnum*num) ;	
		}
		else if(v==1)					//横向前一个方块的右边为:F  
		{
			if(v == get_val(map[row][col][(i+3)%4]))		//当前方块的左边为F 
			{
				vis[i] = true ;
				int n_v = get_val(map[row][col][(i+1)%4]) ;
				int pre_a = get_val(map[row][col][i]);
				int now_a = get_val(map[row][col][(i+2)%4]);
				long long num = 1 ;
				for(int j=i+1;j<4;j++)
				{
					if(ok(row,col,i,j))
					{
						num++ ;
						vis[j] = true ;	
					}	
				}
				dfs(n_v,col+1,now_s*3+now_a,pre_s*3+pre_a,nnum*num) ;	
			}	
		}
		else if(v == 2)
		{
			if(v == get_val(map[row][col][(i+3)%4]))
			{
				vis[i] = true ;
				int n_v = get_val(map[row][col][(i+1)%4]) ;
				int pre_a = get_val(map[row][col][i]);
				int now_a = get_val(map[row][col][(i+2)%4]);
				long long num = 1 ;
				for(int j=i+1;j<4;j++)
				{
					if(ok(row,col,i,j))
					{
						num++ ;
						vis[j] = true ;	
					}	
				}
				dfs(n_v,col+1,now_s*3+now_a,pre_s*3+pre_a,nnum*num) ;		
			}
		}
		else if(v == 0)
		{
			if(v == get_val(map[row][col][(i+3)%4]))
			{
				vis[i] = true ;
				int n_v = get_val(map[row][col][(i+1)%4]) ;
				int pre_a = get_val(map[row][col][i]);
				int now_a = get_val(map[row][col][(i+2)%4]);
				long long num = 1 ;
				for(int j=i+1;j<4;j++)
				{
					if(ok(row,col,i,j))
					{
						num++ ;
						vis[j] = true ;	
					}	
				}
				dfs(n_v,col+1,now_s*3+now_a,pre_s*3+pre_a,nnum*num) ;		
			}
		}
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	for(int ncase=1;ncase<=T;ncase++)
	{
		printf("Case %d: ",ncase);
		scanf("%d %d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
			{
				scanf("%s",map[i][j]);	
			}	
		}
		all = 1 ;				//总的状态数; 
		for(int i=1;i<=m;i++)
		{
			all *= 3 ;
		}
		for(int i=0;i<all;i++)
		{
			dp[0][i] = 1 ;	
		}
		now = 0 ; pre = 1 ;			//滚动数组优化空间; 
		for(int i=1;i<=n;i++)
		{
			row = i ;
			now ^= 1 ; pre ^= 1 ;
			memset(dp[now],0,sizeof(dp[now]));
			dfs(3,1,0,0,1) ;			
		}
		
		long long  ans = 0 ;
		for(int i=0;i<all;i++)
		{
			ans = (ans + dp[now][i]) % MOD ;	
		}
		
		printf("%lld\n",ans);
	}
	return 0;
}



你可能感兴趣的:(FZ_2011 Carcassonne 状态DP)