链接:http://acm.hdu.edu.cn/showproblem.php?pid=1693
题意:给出一块r*c的地,(r,c<=11),其中有的土地上种上了树,有些没有种上树,只能在种上树的地上走,通过走若干个回路,来走遍所有种树的土地。问有多少种走法。
思路:无论如何还是要先提供一个链接:http://wenku.baidu.com/view/4fe4ac659b6648d7c1c74633.html,是国家队论文,里面对于要提及的概念讲解的十分清楚。
dp的过程是从左上角的点到右下角的点进行状态压缩dp。dp[i][j][k]表示第i行第j列时,轮廓线上从左到右是否存在插头的二进制状态k(0表示轮廓线上不存在插头,1表示轮廓线上存在插头)。
每行第零个状态是由右上角的状态第c个状态得到,具体过程是dp[i][0][j<<1]=dp[i-1][c][j]; 这是因为轮廓线从每行的第最后一种状态变成了每行的第一种状态(画画就明白了)。
状态转移也是根据插头的对应情况来写的,是由当前格子的下方轮廓线和右侧轮廓线上是否存在插头来决定。
如果下方和右侧都存在插头,那么左侧和上方就都不能存在插头,所以状态时由上一个状态中对应两位都为0的情况得到。
如果下方和右侧都不存在插头,那么左侧和上方就都必然存在插头,所以状态时由上一个状态中对应两位都为1的情况得到。
如果下方存在插头,右侧不存在插头,或者是右侧存在插头,下方不存在插头,那么就可能存在上方存在插头或者是左侧存在插头两种情况,分别寻找对应状态加上即可。
P.S.果然原来插头DP的名字是基于连通性状态压缩的动态规划,本质还是状压...
代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ctype.h>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define eps 1e-8
#define INF 0x7fffffff
#define maxn 13
#define PI acos(-1.0)
#define seed 31//131,1313
//#define LOCAL
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
LL dp [maxn][maxn][1<<maxn] ;
int M [maxn][maxn] ;
int row,col;
LL solve(int r,int c)
{
int tot=1<<(c+1);
memset(dp,0,sizeof(dp));
dp[0][c][0]=1;
for(int i=1; i<=r; i++)
{
for(int j=0; j<tot; j++)
dp[i][0][j<<1]=dp[i-1][c][j];
for(int j=1; j<=c; j++)
{
int st1=(1<<j),st2=(1<<(j-1));
for(int k=0; k<tot; k++)
{
if(M[i][j]==1)
{
if((k&st1)==0&&(k&st2)==0)
dp[i][j][k]=dp[i][j-1][k+st1+st2];
else if((k&st1)!=0&&(k&st2)!=0)
dp[i][j][k]=dp[i][j-1][k-st1-st2];
// else
// {
// dp[i][j][k]+=dp[i][j-1][k];
// dp[i][j][k]+=dp[i][j-1][k^st1^st2];
// }//与下述状态转移等效,且更优美
else if((k&st1)==0&&(k&st2)!=0)
{
dp[i][j][k]+=dp[i][j-1][k-st2+st1];
dp[i][j][k]+=dp[i][j-1][k];
}
else
{
dp[i][j][k]+=dp[i][j-1][k-st1+st2];
dp[i][j][k]+=dp[i][j-1][k];
}
}
else if((k&st1)==0&&(k&st2)==0)
dp[i][j][k]=dp[i][j-1][k];
}
}
}
return dp[r][c][0];
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
#endif
int T;
scanf("%d",&T);
for(int ii=1;ii<=T;ii++)
{
scanf("%d%d",&row,&col);
for(int i=1; i<=row; i++)
for(int j=1; j<=col; j++)
scanf("%d",&M[i][j]);
printf("Case %d: There are %I64d ways to eat the trees.\n",ii,solve(row,col));
}
return 0;
}