hdu1693 插頭dp
阿新 • • 發佈:2019-02-01
學習插頭dp 真是艱難,做的第一道題。
題意,現在給一個n*m的矩陣 矩陣中有一些障礙,障礙不能通過,現在要把能通過的點全部連起來,形成哈密頓迴路,但是可以連成多個哈密頓迴路,問有多少種方法。
直接說做法吧,首先是用輪廓線不懂的,可以先看看這
看圖
在輪廓線上每一位如果有一個插頭那麼這一位就表示位1沒有就表示為0,插頭是是在迴路上的線,如果有插頭表示上一格經過了這一條邊,如圖狀態表示為101111當前轉移的格子為第二行第三個,轉移的時候主要看這個格子左邊和上面的插頭,如果都為1表示這兩個連線到一起了,如果其中一個格子為1另一個為0 那麼就是有兩個出口如圖,注意一下如果當前格子是障礙的話要特殊處理一下,只有在上插頭和左插頭是0的時候才能轉移,因為沒有辦法經過這個格子
然後特殊處理一下最後一格,一行處理完後要把輪廓線移動到前面來,如圖
具體操作看程式碼:
#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;
}