恩恩,最近在练习状态压缩DP,这道题目一开始就想出状态转移方程了,可是爆了MLE。。。一下子还真的没有想到怎么去优化这个东西。。。
状态压缩的推导方法和POJ 1321还有POJ 3254 的差不多 这里是POJ 1321 的解题报告:http://blog.csdn.net/good_night_sion_/article/details/52432629
最后看了别人的题解,发现别人使用的滚动数组解决这个问题的,确实滚动数组可以用来优化,然后还发现了一种连滚动数组都不需要的解法,其实我感觉就和背包的问题的空间优化差不多,只是披上了状态压缩的外衣。。恩,果然还是需要多多练习才是。
这是没有用滚动数组的写法,76ms过掉了
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,arr[25][25],board[(1<<20)+10],lim,ans;
int main(){
// printf("%lld\n",sizeof(board));
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;++i){
scanf("%d",&arr[i][0]);
for(int j=1;j<=arr[i][0];++j)
scanf("%d",&arr[i][j]);
}
if(n>m){
printf("0\n");
continue;
}
lim=1<<m;
board[0]=1;
for(int i=1;i<=n;++i){
for(int j=lim-1;j>=0;--j){
if(board[j]==0)continue;
for(int k=1;k<=arr[i][0];++k)
if(((1<<arr[i][k]-1)&j)==0)
board[j|(1<<arr[i][k]-1)]+=board[j];
board[j]=0;
}
}
for(int i=0;i<lim;++i)
ans+=board[i];
printf("%d\n",ans);ans=0;
memset(board,0,sizeof(int)*lim);
}
return 0;
}
这下是用了滚动数组的写法,我用了500ms过掉了,我觉得应该还可以优化,但是POJ挂掉了,没办法验证。。
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,arr[25][25],board[2][(1<<20)+10],bitcount(int x),lim,ans;
int main(){
// printf("%lld\n",sizeof(board));
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=1;i<=n;++i){
scanf("%d",&arr[i][0]);
for(int j=1;j<=arr[i][0];++j)
scanf("%d",&arr[i][j]);
}
if(n>m){
printf("0\n");
continue;
}
lim=1<<m;
board[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<lim;++j){
if(board[1-(i&1)][j]==0)continue;
for(int k=1;k<=arr[i][0];++k)
if(((1<<arr[i][k]-1)&j)==0)
board[i&1][j|(1<<(arr[i][k]-1))]+=board[1-(i&1)][j];
}
}
for(int i=0;i<lim;++i)
if(bitcount(i)==n)
ans+=board[n&1][i];
printf("%d\n",ans);ans=0;
for(int i=0;i<=1;++i)
memset(board[i],0,sizeof(int)*lim);
}
return 0;
}
int bitcount(int x){
int times=0;
for(;x;x>>=1)if(x&1)++times;
return times;
}
注意!代码里面的有一个if(board[x]==0)continue;这句代码很重要!可以明显的减少很多不必要的操作,滚动数组没有想到这个优化的情况下时间爆长。。。我用了3700ms+才过掉了,,要是卡时间就完蛋了。