[HDU1693] Eat the Trees (插头DP)
由周自神讲的插头DP模板题,人生第一道插头DP题纪念一下
状态非常简单,只用0,1即可,
要注意行间转移时不能(要是数组够用这么干也可以,因为DP中不会考虑到这个状态,所以对答案无影响):
for(int j=0;j<bin[m+1];j++)
这样会把上一层状态的最后一个搞到下一层造成累赘
正确应为:
for(int j=0;j<bin[m];j++)
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<string>
#include<cstring>
#define int long long
#define m(a) memset(a,0,sizeof(a))
#define AA cout<<"Alita"<<endl
using namespace std;
const int N=20;
int S,T,n,m,a[N][N],bin[N],f[N][N][5000];
void Plug(int x,int y)
{
int p1=bin[y-1],p2=bin[y];
for(int i=0;i<bin[m+1];i++)
{
int l=i&p1,r=i&p2;
if(!a[x][y])
{
if((!l)&&(!r)) f[x][y][i]+=f[x][y-1][i]; //继承
else f[x][y][i]=0; //非法
}
else
{
if(l&&r) f[x][y][i^p1^p2]+=f[x][y-1][i]; //连接
else if(l||r)
{
f[x][y][i]+=f[x][y-1][i]; //拐弯
f[x][y][i^p1^p2]+=f[x][y-1][i]; //直走
}
else
{
f[x][y][i^p1^p2]+=f[x][y-1][i]; //创造
}
}
}
}
void work()
{
memset(f,0,sizeof(f));
scanf("%lld%lld",&n,&m);
S=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%lld",&a[i][j]);
S|=a[i][j];
}
}
if(!S)
{
puts("1");
return;
}
f[1][0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++) Plug(i,j);
for(int j=0;j<bin[m+1];j++)
{
f[i+1][0][j<<1]=f[i][m][j];
}
}
printf("%lld\n",f[n][m][0]);
}
signed main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
bin[0]=1;
for(int i=1;i<=14;i++) bin[i]=bin[i-1]*2;
scanf("%lld",&T);
while(T--) work();
return 0;
}