hdu 1693 Eat the Trees 轮廓线 插头dp

来源:互联网 发布:数据库证书 编辑:程序博客网 时间:2024/06/10 00:36

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1693

题目意思:

给n*m的方格,有些方格有障碍,问有多少种方式能使所有的非障碍点都在某个环上,环可以有多个。

解题思路:

轮廓线dp.

dp[i][j][s]表示到达第i行第j列,轮廓线上插头的状态为s时的总的方案数。

对于当前第j列,状态(bool) s&(1<<j)表示当前格的右插头,(bool) s&(1<<(j-1)表示当前格的下插头。

容易知:

当前格为通行状态时:00<=11     01<=10,01      10<=10,01     11<=00 

当前格为障碍状态时:00<=00     其他为0

初始化为dp[0][m][0]=1 ;表示初始状态

上一行的最后一列的状态可以左移一位作为下一行的初始状态。

因为对于每一行的最后一列状态有效状态 K0K1K2..Km  Km一定为0,不可能有右插头

对于每一行的开始一列的前一状态 K0K1K2..Km K0一定为0,不可能存在第一格的左插头。

所以可以左移一位,作为下一行的初始状态。(注意下标从左置右为从小到大)

代码:

#include<iostream>#include<cmath>#include<cstdio>#include<cstdlib>#include<string>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<set>#include<stack>#include<list>#include<queue>#define eps 1e-6#define INF 0x1f1f1f1f#define PI acos(-1.0)#define ll __int64#define lson l,m,(rt<<1)#define rson m+1,r,(rt<<1)|1using namespace std;/*freopen("data.in","r",stdin);freopen("data.out","w",stdout);*/ll dp[15][15][1<<15];int save[15][15];int main(){   int t,n,m;   scanf("%d",&t);   for(int ca=1;ca<=t;ca++)   {      scanf("%d%d",&n,&m);      for(int i=1;i<=n;i++)         for(int j=1;j<=m;j++)            scanf("%d",&save[i][j]);      dp[0][m][0]=1; //初始化状态      for(int i=1;i<=n;i++)      {         for(int j=0;j<(1<<(m+1));j++)  //换行的时候,只需将状态左移一位            dp[i][0][j<<1]=dp[i-1][m][j];//从k0k1k2...k(m-1)0 到0k1k2k3...km,只需左移一位         for(int j=1;j<=m;j++)         {            for(int k=0;k<(1<<(m+1));k++)            {               int p=1<<j; //当前格子的右插头 //分别从上一格子的 相对于当前格子的左插头和上插头递推过来               int q=p>>1; //当前格子的下插头               bool x=k&p; //是否有插头,将多位转化为1位               bool y=k&q; //是否有插头               if(save[i][j]) //可通               {                  dp[i][j][k]=dp[i][j-1][k^p^q]; //相当于这两位取反 00<=11                                                                  //01<=10 10<=01 11<=00                                                   //对于每个状态都有一条路联通,另外01<=01 10<=10                  if(x!=y) //新增加的情况 10<=10 01<=10                     dp[i][j][k]+=dp[i][j-1][k];               }               else //有障碍物               {                  if(x==0&&y==0) //均为0                     dp[i][j][k]=dp[i][j-1][k]; //前一格子也为0,0,前面一块可以连通                  else                     dp[i][j][k]=0;  //不为0,对于这种状态只能标志为0               }            }         }      }      printf("Case %d: There are %I64d ways to eat the trees.\n",ca,dp[n][m][0]);     // printf("%d\n",dp[n][m][0]); //轮廓线为0(没有插头)的情况   }   return 0;}


原创粉丝点击