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;
}