【HDU1693】Eat the Trees-插头DP

测试地址:Eat the Trees

题目大意:一个N*M的矩形场地,有一些格子不能走,要求走若干条回路使得走过每个格子一次且仅一次,求方案数。

做法:一看到棋盘类型的题目和很小的数据范围就想到插头DP。因为题目中要求若干条回路,所以轮廓线状态定义直接和骨牌覆盖问题一样,插头为1表示这个位置需要一个格子来承接,插头为0表示不需要。设f[i][j][state]为递推到第i行第j列的格子,轮廓线状态为state的方案数,逐格递推,枚举轮廓线状态,并找出第j和第j+1位(从左到右),称作关键位,判断:如果当前格子不能走,那么仅当两个关键位都为0时才更新状态,此时f[i][j][state]=f[i][j-1][state],否则f[i][j][state]=0。如果当前格子能走,按照关键位分成3种情况:两个关键位都为0,两个关键位都为1或者其中一个关键位为0,另一个关键位为1,分情况状态转移即可。和一般的插头DP问题一样,要注意递推到一行最后一个格子时轮廓线状态的转移。

我傻逼的地方:轮廓线状态是m+1位的,所以数组要开到2^(m+1)那么多,而不是2^m......结果我交上去竟然提示TLE而不是RE,很迷......

以下是本人代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
int n,m,T,w[20][20],now,past;
ll f[2][5010];

int main()
{
  scanf("%d",&T);
  for(int t=1;t<=T;t++)
  {
    memset(f,0,sizeof(f));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++)
	    scanf("%d",&w[i][j]);
	f[0][0]=1;
	now=1,past=0;
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++)
	  {
	    for(int k=0;k<(1<<(m+1));k++)
		{
		  int bit1=k&(1<<(j-1)),bit2=k&(1<<j);
		  if (!bit1&&!bit2)
		  {
		    if (!w[i][j]) f[now][k]=f[past][k];
			else f[now][k]=f[past][k+(1<<(j-1))+(1<<j)];
		  }
		  else if (!w[i][j]) {f[now][k]=0;continue;}
		  else if (bit1&&bit2) f[now][k]=f[past][k-(1<<(j-1))-(1<<j)];
		  else
		  {
		    if (bit1) f[now][k]=f[past][k]+f[past][k-(1<<(j-1))+(1<<j)];
			else f[now][k]=f[past][k]+f[past][k-(1<<j)+(1<<(j-1))];
		  }
		}
		if (j==m)
		{
		  for(int k=(1<<(m+1));k>=0;k--)
		  {
		    if (k%2) f[now][k]=0;
			else f[now][k]=f[now][k>>1];
		  }
		}
		swap(now,past);
	  }
	printf("Case %d: There are %lld ways to eat the trees.\n",t,f[past][0]);
  }
  
  return 0;
}


posted @ 2017-03-26 17:07  Maxwei_wzj  阅读(77)  评论(0编辑  收藏  举报