HDU 4064 Carcassonne

Carcassonne
Problem Description
Carcassonne is a tile-based board game for two to five players.
Square tiles are printed by city segments,road segments and field segments. 

The rule of the game is to put the tiles alternately. Two tiles share one edge should exactly connect to each other, that is, city segments should be linked to city segments, road to road, and field to field. 

To simplify the problem, we only consider putting tiles:
Given n*m tiles. You can rotate each tile, but not flip top to bottom, and not change their order. 
How many ways could you rotate them to make them follow the rules mentioned above?
 

 Input

The first line is a number T(1<=T<=50), represents the number of case. The next T blocks follow each indicates a case.
Each case starts with two number N,M(0<N,M<=12)
Then N*M lines follow,each line contains M four-character clockwise.
'C' indicate City.
'R' indicate Road.
'F' indicate Field.
 

 Output

For each case, output the number of ways mod 1,000,000,007.(as shown in the sample output)
 

 Sample Input

3
1 1
RRRR
1 2
RRRF FCCC
8 8
FCFF RRFC FRCR FRFR RCCR FFCC RRFF CRFR
FRRC FRFR CCCR FCFC CRRC CRRR FRCR FRFR
RRCR FRRR CCCR FFFC RRFF RFCR CCFF FCCC
CFCF RRFF CRFR FFRR FRRF CCRR FFFC CRRF
CFRR FFFF FFFF RRFF RRRR RCRR FFCC RFRF
RRCF FRFR FRRR FRFR RCCR RCCC CFFC RFRF
CFCF FRFF RRFF FFFF CFFF CFFF FRFF RFRR
CCRR FCFC FCCC FCCC FFCC FCCF FFCC RFRF
 

Sample Output

Case 1: 4
Case 2: 1
Case 3: 1048576
 

Source

 
 
题意:给出一块n×m的拼图板,每块拼图的四条边都代表着一些景物,C表示城市,R表示路,F表示田野。很显然,对于互不相同的两块拼图只有边缘代表的景物相同才能拼在一起。规定拼图本身位置不能变,只能转向,问有多少种拼法。对于每一组输入,是n行m列长度为4的字符串。每一个字符串的含义是按顺时针顺序给出拼图边缘代表的景物。
 
题解:对于n,m那么小的数据,想必能很快想到搜索。但是很显然,搜索的状态数太多了,不便于快速解答。
于是我们便想到了用状态压缩DP来解决此题。
一般很多状压都是二进制的,但针对本题三种景物,我们自然选择三进制的状压。
对于每一行,状态总个数的最大值为312=531441,这样不会特别大,在可接受范围内。
接下来讲如何状压。
首先我们应该想到对于每一行,我们可以根据上一行底端出现的状态数去推导下一行的状态数,因为上一行的底端就是下一行的顶端,这样我们就能连贯地求出下一行的底端,并把状态数不断下推,直到最后一行。
紧接着就是处理单独的一行。对于单独的一行就包含很多的状态,我们用DFS处理更加方便。首先此行第一列肯定朝四个方向摆都行,我们就直接摆,之后的摆法必须建立于后一列的左端与前一列的右端相等,这样摆完一行后,我们把这一行顶端的情况对应到上一行底端的情况,并把上一行的状态数加入这一行。
梳理好这两步,该题的主要框架就建立好了。
接下来就是一些细节,下面是我的体验:
1、预处理3的幂次方时,我naive地处理到11以为就够了,于是WA。
2、最后输出时想当然在case后加了#号,于是WA。
3、对于一块拼图,如果4条边都相等,说明无论朝哪个方向摆都能产生相同的贡献,且这样相同的贡献重复了4次,所以我们记录一个mul,遇到4条边相同的拼图可以直接将mul乘以4。到边界处理时记牢把上一行的状态数乘以mul再加进这一行的状态统计。这样就可以获得强大的剪枝效果。如果没有这个剪枝就会TLE。当然后面的DFS结束后,要记牢把mul除以4还原。
4、注意f[i][j]表示的含义是在第i行出现了状态j,且j代表第i行底端的状态。声明这里是为了防止有人把DFS中遇到边界处理记录状态理解错。
5、如果空间开太大会MLE,因此要用滚动数组记录状态。
6、没有了。
7、真的没有了。
好了,下面附上我的代码。
 1 #include<bits/stdc++.h>
 2 #define mod 1000000007
 3 using namespace std;
 4 const int N=15,S=531441;
 5 int ca,t,n,m,cur,mul,base[13],g[N][N][4];
 6 long long ans,f[2][S];
 7 char s[5];
 8 int change(char x){
 9     return x=='C'?0:(x=='R'?1:2);
10 }
11 void dfs(int c,int num,int up,int down,int right)
12 {
13     if (num>m)
14     {
15         if (c==1) f[cur][down]=(f[cur][down]+mul)%mod;
16         else f[cur][down]=(f[cur][down]+f[cur^1][up]*mul)%mod;
17         return;
18     }
19     if (g[c][num][0]==g[c][num][1]&&g[c][num][1]==g[c][num][2]&&g[c][num][2]==g[c][num][3])
20     {
21         int add=g[c][num][0]; mul*=4;
22         if (right==-1||right==add) dfs(c,num+1,up*3+add,down*3+add,add);
23         mul/=4; return;
24     }
25     for (int i=0;i<4;++i)
26     {
27         int U=g[c][num][i];
28         int R=g[c][num][(i+1)%4];
29         int D=g[c][num][(i+2)%4];
30         int L=g[c][num][(i+3)%4];
31         if (right==-1||right==L) dfs(c,num+1,up*3+U,down*3+D,R);
32     }
33 }
34 int main()
35 {
36     scanf("%d",&t); base[0]=1;
37     for (int i=1;i<=12;++i)
38     base[i]=base[i-1]*3;
39     while (t--)
40     {
41         scanf("%d%d",&n,&m);
42         for (int i=1;i<=n;++i)
43         for (int j=1;j<=m;++j)
44         {
45             scanf("%s",s);
46             for (int k=0;k<4;++k)
47             g[i][j][k]=change(s[k]);
48         }
49         memset(f,0,sizeof(f));
50         cur=ans=0;
51         for (int i=1;i<=n;++i)
52         {
53             cur^=1; mul=1;
54             dfs(i,1,0,0,-1);
55             for (int j=0;j<base[m];++j)
56             f[cur^1][j]=0;
57         }
58         for (int i=0;i<base[m];++i)
59         ans=(ans+f[cur][i])%mod;
60         printf("Case %d: %d\n",++ca,ans);
61     }
62     return 0;
63 }
View Code

 

posted @ 2017-10-29 19:24  氟铷氡氦  阅读(421)  评论(0编辑  收藏  举报