学习插头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;
}