Time Limit:500MS | Memory Limit:32768KB | 64bit IO Format:%I64d & %I64u |
Description
We wish to tile a grid 4 units high and N units long with rectangles (dominoes) 2 units by one unit (in either orientation). For example, the figure shows the five different ways that a grid 4 units high and 2 units wide may be tiled.
Write a program that takes as input the width, W, of the grid and outputs the number of different ways to tile a 4-by-W grid.
![](https://i-blog.csdnimg.cn/blog_migrate/96e37716a8ff4c9b5d1860dc25ec76f2.jpeg)
Write a program that takes as input the width, W, of the grid and outputs the number of different ways to tile a 4-by-W grid.
Input
The first line of input contains a single integer N, (1 ≤ N ≤ 1000) which is the number of datasets that follow.
Each dataset contains a single decimal integer, the width, W, of the grid for this problem instance.
Each dataset contains a single decimal integer, the width, W, of the grid for this problem instance.
Output
For each problem instance, there is one line of output: The problem instance number as a decimal integer (start counting at one), a single space and the number of tilings of a 4-by-W grid. The values of W will be chosen so the count will fit in a 32-bit integer.
Sample Input
3 2 3 7
Sample Output
1 5 2 11 3 781
Source
2008 “Shun Yu Cup” Zhejiang Collegiate Programming Contest - Warm Up(1)
解析
状态压缩DP入门题。0表示会占用下一行,1表示不会占用下一行。
那么上图的5种状态压缩后应该是:
0 1 0 1 0 1 1 1 1 1
0 1 0 1 1 1 1 1 1 1
0 1 1 1 1 1 0 1 1 1
0 1 1 1 0 1 0 1 1 1
那么满足什么样的条件才能将两列排在一起呢?
如果上一行是a,下一行是b
1.上一行有0的格子下一行一定要是1。上一行是1的地方,下一行既可以是1也可以是0。即 a | b = 2^4 - 1
2.上一行是1 下一行也是1的格子一定要成对地挨在一起。(如果我用check(x)来验证x中的1是不是成对地挨在一起)那么必须满足check(a&b)
当且仅当同时满足条件1和条件2才能将两列排在一起。
我们把每列都用一个整数来表示,就可以用动态规划了。f [ i ] [ j ]表示第i列的状态为j的方案总数。
转移方程:if( k可以排在j的后面 ) f [ i ] [ k ] += f [ i-1 ] [ j ]
输出f [ N ] [ 2^4-1 ] 因为最后一排的状态必须是1111
解析
状态压缩DP入门题。0表示会占用下一行,1表示不会占用下一行。
那么上图的5种状态压缩后应该是:
0 1 0 1 0 1 1 1 1 1
0 1 0 1 1 1 1 1 1 1
0 1 1 1 1 1 0 1 1 1
0 1 1 1 0 1 0 1 1 1
那么满足什么样的条件才能将两列排在一起呢?
如果上一行是a,下一行是b
1.上一行有0的格子下一行一定要是1。上一行是1的地方,下一行既可以是1也可以是0。即 a | b = 2^4 - 1
2.上一行是1 下一行也是1的格子一定要成对地挨在一起。(如果我用check(x)来验证x中的1是不是成对地挨在一起)那么必须满足check(a&b)
当且仅当同时满足条件1和条件2才能将两列排在一起。
我们把每列都用一个整数来表示,就可以用动态规划了。f [ i ] [ j ]表示第i列的状态为j的方案总数。
转移方程:if( k可以排在j的后面 ) f [ i ] [ k ] += f [ i-1 ] [ j ]
输出f [ N ] [ 2^4-1 ] 因为最后一排的状态必须是1111
#include<cstdio>
#include<bitset>
#include<cstring>
#include<set>
using namespace std;
int N;
int f[1010][16];//状压描述:0表示会占用下一行,1表示不会占用下一行。
void readdata()
{
scanf("%d",&N);
memset(f,0,sizeof(f));
}
bool check(int x)//检查x是否满足两个1排在一起的规矩
{
for(int i=1;i<16;i=i<<1)
if(x&i) if((i<<1)<16 &&(x&(i<<1))) i=i<<1; else return 0;
return 1;
}
bool legal[100];//用legal记下合法状态可以加速
void work()
{
if(N==1) {printf("1"); return;}
//初始化
memset(legal,false,sizeof(legal));
for(int j=0;j<16;j++)//枚举状态,找到所有的合法状态
if(check(j)) {f[1][j]=1; legal[j]=true;}
//DP
for(int i=2;i<=N;i++)
for(int j=0;j<16;j++)//枚举上一状态
if(f[i-1][j])
for(int k=0;k<16;k++)//枚举当前状态
if(legal[j&k] && (j|k)==15) f[i][k]+=f[i-1][j];
/*两种特殊情况
0 1 0 1 0 1 1
1 0 1 1 0 1 1
1 0 1 1 1 0 1
0 1 0 1 1 0 1
*/
printf("%d",f[N][15]);
}
int main()
{
freopen("poj3797.in","r",stdin);
int T;scanf("%d",&T);
for(int i=1;i<=T;i++)
{
printf("%d ",i);
readdata();
work();
printf("\n");
}
while(1);
return 0;
}