EOJ:Mosaic
Mosaic
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submits: 13 | Accepted: 5 |
Description
He can rotate the second kind of tile in any of four ways. He wants to fill the entire space with tiles, leaving no untiled gaps. Now, he wonders how many different patterns he can form. He considers two mosaics to be the same only if they have exactly the same kinds of tiles in exactly the same positions. So, if a rotation or a reflection of a pattern has tiles in different places than the original, he considers it a different pattern. The following are examples of 4” x 16” mosaics, and even though they are all rotations or reflections of each other, the architect considers them to be four different mosaics:
Input
Output
Sample Input
2 10
4 16
4 50
0 0
Sample Output
25
366318
574777
原题地址:http://www.cn210.com/onlinejudge/problemshow.php?pro_id=241
_________________________________________________________________________________________________________
题解:
原来做过类似的题,不过以前的题没有那么麻烦,给的小方块都是1*2的……这题的方块有五种。
由于行数最大才只有10,所以可以一列一列的DP,每列用一个十进制数表示状态(转换为二进制,若该位为1,则这列上的那一行被覆盖了)
转移方程:DP[I][J]=求和(DP[I-1][K]) (所有可以转移到J的K) DP[I][J]表示,到第I列,前面I-1列已经全部填满,且第I列的状态是J的方案总数。
最主要的问题就是判断K和J是否能转移,假设K能够转移到J,则K和J连一条边。由于一个K可以通过不同方式转移到J,所以还要记录权值。
如图:
0 1 ____ 0 1
0 0 | 0 1
K J 这样表示放了一块| 的砖 K J 这样表示放了块2*2的砖。(保证第K列及前面所有列都已经填满)
我们通过深搜来连边。先穷举K的值,然后通过搜索来填方块,使得第K列都被覆盖,这时我们就能得到第J列的覆盖情况,将其转换成十进制就是J列的状态。
1 #include<stdio.h>
2 #include<memory.h>
3 #define p 1000000
4 int dp[501][1028],sum[1028],nbs[12][1028],ev[12][20000],ew[12][20000],next[12][20000];
5 int i,j,k,t,n,m,num;
6 int a[1028],b[1028];
7 void change(int num,int n)
8 {
9 int i,x,l,tmp[1025];
10 x=num;
11 l=0;
12 memset(tmp,0,sizeof(tmp));
13 while (x!=0)
14 {
15 l++;
16 tmp[l]=x%2;
17 x=x/2;
18 }
19 for (i=1;i<=n;i++)
20 a[i]=tmp[n+1-i];
21 }
22 int rechange(int n)
23 {
24 int temp,x,i;
25 temp=0;
26 x=1;
27 for (i=n;i>=1;i--)
28 {
29 temp=temp+b[i]*x;
30 x=x*2;
31 }
32 return temp;
33 }
34 void dfs(int t,int n)
35 {
36 int x,i;
37 i=t;
38 while (a[i]!=0&&i<=n)
39 i++;
40 if (i>n)
41 {
42 x=rechange(n);
43 sum[x]+=1;
44 return;
45 }
46 if (i>1&&b[i]==0&&b[i-1]==0)
47 {
48 b[i]=1;b[i-1]=1;
49 dfs(i+1,n);
50 b[i]=0;b[i-1]=0;
51 }
52 if (i+1<=n&&b[i]==0&&b[i+1]==0)
53 {
54 b[i]=1;b[i+1]=1;
55 dfs(i+1,n);
56 b[i]=0;b[i+1]=0;
57 }
58 if (i+1<=n&&a[i+1]==0&&b[i]==0)
59 {
60 b[i]=1;
61 dfs(i+2,n);
62 b[i]=0;
63 }
64 if (i+1<=n&&a[i+1]==0&&b[i+1]==0)
65 {
66 b[i+1]=1;
67 dfs(i+2,n);
68 b[i+1]=0;
69 }
70 if (i+1<=n&&a[i+1]==0&&b[i]==0&&b[i+1]==0)
71 {
72 b[i+1]=1;b[i]=1;
73 dfs(i+2,n);
74 b[i+1]=0;b[i]=0;
75 }
76 }
77 int pre()
78 {
79 memset(nbs,0,sizeof(nbs));
80 for (k=1;k<=10;k++) //枚举行数
81 {
82 t=(1<<k)-1;
83 num=0;
84 for (i=0;i<=t;i++) //枚举第一个点
85 {
86 memset(b,0,sizeof(b));
87 memset(sum,0,sizeof(sum));
88 change(i,k); //计算二进制
89 dfs(1,k); //搜索,可以与第一个点相连的点
90 for (j=0;j<=t;j++)
91 if (sum[j]>0)
92 {
93 next[k][++num]=nbs[k][i];
94 nbs[k][i]=num;
95 ev[k][num]=j;ew[k][num]=sum[j];
96 }
97
98 }
99 }
100 int main()
101 {
102 pre();//预处理,计算出不同行数下,各个点之间的边及权值
103 //freopen("a.txt","w",stdout);
104 while (scanf("%d%d",&n,&m)!=EOF)
105 {
106 if (n==0&&m==0) break;
107 t=(1<<n)-1;
108 memset(dp,0,sizeof(dp));
109 for (i=nbs[n][t];i;i=next[n][i])
110 dp[1][ev[n][i]]=ew[n][i];
111 for (i=1;i<m;i++)
112 for (j=0;j<=t;j++)
113 if (dp[i][j]>0)
114 for (k=nbs[n][j];k;k=next[n][k])
115 dp[i+1][ev[n][k]]=(dp[i+1][ev[n][k]]+(dp[i][j]*ew[n][k])%p)%p;
116 printf("%d\n",dp[m][t]);
117 }
118 return 0;
119 }