插头DP学习
队内没人会插头DP,感觉这个不会不行。。。所以我还是默默去学了一下,
学了一天,感觉会了一点。对于每一行,一共有j+1个插头,如果是多回路类的题目,
比较简单,可以用1表示有插头,0表示没有插头,这样就可以愉快转移了,
对于当前出来的位置(i,j),与它有关的插头有j-1和j 那么我们可以枚举状态经行转移。
对于单回路的问题。。。。只是了解思想,目前还不会写,太笨了=_=
poj 2411 Mondriaan's Dream
题意:用1*2小方块组成n*m矩阵有多少种组成方式
思路:我们从上到下处理每一行,对于当前处理的位置 (i,j),枚举它的插头状态,0~(1<<(m+1));
假设状态是 sta,如果sta表示的状态没有j-1和j 插头(即(sta&(1<<(j-1))) == 0 &&(sta&(1<<j)) == 0)
那么转移到它的状态有两种,j-1插头有或者j插头存在,
如果sta表示的状态都有j-1和j 插头,这样状态是不合法的,直接=0
如果sta表示的状态j 插头,表示是平放方块,这个状态只能由j-1,j插头不存在状态转移过来
如果sta表示的状态j-1 插头,表示是竖放方块,这个状态只能由j-1,j插头不存在状态转移过来
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define INF 999999 #define maxn 200001 #define mod 1000000007 #define INF 0x3f3f3f3f using namespace std; int mat[15][15]; LL dp[12][12][(1<<13)+10] ; int main() { int i , n ,m , j, k ,case1=0; int len , T ,x ,y ,sta ; while(scanf("%d%d",&n,&m)!=EOF) { if(n+m==0)break ; if((n*m)&1) { puts("0") ; continue ; } memset(dp,0,sizeof(dp)) ; len=(1<<(m+1)); dp[0][m][0]=1; for( i = 1 ; i <= n ;i++) { for( j = 0 ; j < len ;j++) dp[i][0][(j<<1)]=dp[i-1][m][j] ; for( k = 1 ; k <= m ;k++) { for( sta = 0 ; sta < len ;sta++) { y = (1<<k) ; x = (1<<(k-1)) ; if((sta&x) != 0 &&(sta&y) != 0 ) { dp[i][k][sta]=0; } else if((sta&x)==0&&0==(sta&y)) { dp[i][k][sta]=dp[i][k-1][sta^x]+dp[i][k-1][sta^y] ; } else if((sta&x)) dp[i][k][sta]=dp[i][k-1][sta^x]; else dp[i][k][sta]=dp[i][k-1][sta^y]; } } } printf("%I64d\n",dp[n][m][0]); } return 0 ; }
hdu 4804 Campus Design
题意:和上题差不多,多了的东西是,有些地方不能放方块,
还有可以放1*1方块,求的是 1*1放的个数限制在c-d内的放的方式有多少种
思路:具体做法和上面也是差不多,
差别:对于1*1放置个数的限制,我们可以开多一维记录1*1用的个数,
对于不能放置的位置(i,j ),合法的状态sta,里面不能有 j-1,j 插头
对于可以放置的位置(1,j) ,状态是 sta,如果sta表示的状态没有j-1和j 插头时
多了一步转移,就是 放置1*1的状态。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define INF 999999 #define maxn 200001 #define mod 1000000007 #define INF 0x3f3f3f3f using namespace std; char mat[105][25]; int dp[2][12][22][(1<<12)+10]; int main() { int i , n ,m , j, k ,case1=0; int len , T ,x ,y ,sta,c,d ,v ; int now,pre; while(scanf("%d%d%d%d",&n,&m,&c,&d )!=EOF) { for( i = 1 ; i <= n ;i++) scanf("%s",mat[i]+1); memset(dp,0,sizeof(dp)) ; len=(1<<(m+1)); dp[0][m][0][0]=1; pre=1; now=0; for( i = 1 ; i <= n ;i++) { swap(pre,now); // memset(dp[now],0,sizeof(dp[now])) ; for( v = 0 ; v <= d ;v++ ) for( j = 0 ; j < len ;j++) dp[now][0][v][(j<<1)]=dp[pre][m][v][j] ; for( k = 1 ; k <= m ;k++) { y = (1<<k) ; x = (1<<(k-1)) ; for( v = 0 ; v <= d ;v++ ) for( sta = 0 ; sta < len ;sta++) { if(mat[i][k]=='0') { if((sta&x)||(sta&y)) { dp[now][k][v][sta]=0; } else dp[now][k][v][sta]=dp[now][k-1][v][sta] ; } else{ if((sta&x) != 0 &&(sta&y) != 0 ) { dp[now][k][v][sta]=0; } else if((sta&x) == 0 &&(sta&y) == 0 ) { dp[now][k][v][sta]=(dp[now][k-1][v][sta^x]+dp[now][k-1][v][sta^y])%mod ; if(v>0)dp[now][k][v][sta]=(dp[now][k][v][sta]+dp[now][k-1][v-1][sta])%mod ; } else if((sta&x)) dp[now][k][v][sta]=dp[now][k-1][v][sta^x]; else dp[now][k][v][sta]=dp[now][k-1][v][sta^y]; } } } } int ans1=0; for( i = c ; i <= d;i++) ans1=(ans1+dp[now][m][i][0])%mod; printf("%d\n",ans1); } return 0 ; }
hdu 1693 Eat the Trees
题意:在n*m的矩阵中,有些格子有树,没有树的格子不能到达,找一条或多条回路,吃完所有的树,求有多少中方法。
思路:看这个博客吧,here
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define INF 999999 #define maxn 200001 #define mod 1000000007 #define INF 0x3f3f3f3f using namespace std; int mat[15][15]; LL dp[12][12][(1<<13)+10] ; int main() { int i , n ,m , j, k ,case1=0; int len , T ,x ,y ,sta ; cin >>T ; while(T--) { scanf("%d%d",&n,&m) ; for( i = 1 ; i <= n ;i++) for( j = 1 ; j <= m ;j++) scanf("%d",&mat[i][j]); memset(dp,0,sizeof(dp)) ; len=(1<<(m+1)); dp[0][m][0]=1; for( i = 1 ; i <= n ;i++) { for( j = 0 ; j < len ;j++) dp[i][0][(j<<1)]=dp[i-1][m][j] ; for( k = 1 ; k <= m ;k++) { for( sta = 0 ; sta < len ;sta++) { y = (1<<k) ; x = (1<<(k-1)) ; if(mat[i][k]) { if((sta&x) && (sta&y)) dp[i][k][sta]=dp[i][k-1][sta^x^y] ; else if((sta&x)==0 && 0== (sta&y)) dp[i][k][sta]=dp[i][k-1][sta^x^y] ; else dp[i][k][sta]=dp[i][k-1][sta^x^y]+dp[i][k-1][sta] ; } else { if((sta&x)==0&&(sta&y)==0) dp[i][k][sta]=dp[i][k-1][sta] ; else dp[i][k][sta]=0; } } } } printf("Case %d: ",++case1); printf("There are %I64d ways to eat the trees.\n",dp[n][m][0]); } return 0 ; }