1. 程式人生 > >hdu1693 插頭dp

hdu1693 插頭dp

學習插頭dp 真是艱難,做的第一道題。
題意,現在給一個n*m的矩陣 矩陣中有一些障礙,障礙不能通過,現在要把能通過的點全部連起來,形成哈密頓迴路,但是可以連成多個哈密頓迴路,問有多少種方法。
直接說做法吧,首先是用輪廓線不懂的,可以先看看
看圖
1
在輪廓線上每一位如果有一個插頭那麼這一位就表示位1沒有就表示為0,插頭是是在迴路上的線,如果有插頭表示上一格經過了這一條邊,如圖狀態表示為101111當前轉移的格子為第二行第三個,轉移的時候主要看這個格子左邊和上面的插頭,如果都為1表示這兩個連線到一起了,如果其中一個格子為1另一個為0 那麼就是有兩個出口如圖,注意一下如果當前格子是障礙的話要特殊處理一下,只有在上插頭和左插頭是0的時候才能轉移,因為沒有辦法經過這個格子
2


然後特殊處理一下最後一格,一行處理完後要把輪廓線移動到前面來,如圖

這裡寫圖片描述
具體操作看程式碼:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cstring>

using namespace std;
#define LL long long
LL dp[2][1<<13]; int main() { int t; scanf("%d",&t); int _case = 0; while(t--) { int dat[15][15]; int n,m; scanf("%d%d",&n,&m); for(int i = 0;i<n;i++) { for(int j = 0;j<m;j++) { scanf
("%d",&dat[i][j]); } } memset(dp,0,sizeof(dp)); int cur = 0; dp[cur][0] = 1; for(int i = 0;i<n;i++) { for(int j = 0;j<m;j++) { for(int k = 0;k<(1<<(m+1));k++) { if(j==m-1 && (k&1))continue; int l = 1<<(m-j); int u = l>>1; if(dat[i][j]) { dp[1-cur][k] = dp[cur][k^l^u]; if(((k&l)&&(k&u))==0 && ((k&l)||(k&u))) { dp[1-cur][k] += dp[cur][k]; } } else { if((k&l)==0 && (k&u)==0) { dp[1-cur][k] = dp[cur][k]; } } } memset(dp[cur],0,sizeof(dp[cur])); cur = cur^1; }//cout << dp[cur][6] << endl; for(int k = 0;k<(1<<(m+1));k++) // 每完成一行後進行轉移 其實就是把狀態右移動一下,首行為0 { if((k&1)==0) dp[1-cur][k>>1] = dp[cur][k]; } //cout << "k" << dp[1-cur][3] << endl; memset(dp[cur],0,sizeof(dp[cur])); cur = 1^cur; } printf("Case %d: There are %lld ways to eat the trees.\n",++_case,dp[cur][0]); } return 0; }