插头DP-1

插头DP HDU – 1693
Eat the Trees 一个方格图,有一些点是障碍,求有用一些回路精确覆盖非障碍点的方案数。n,m≤11

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,T,w[20][20],now,past;   //地图NXM,样例数,地图,两个点(就是下面f的一维下标)
ll f[2][5010];                  //f[][s]表示到当前行S状态为止的方案数
int main(){
    scanf("%d",&T);                 //输出样例数
    for(int t=1;t<=T;t++){          //每个样例
        memset(f,0,sizeof(f));      //清0
        scanf("%d%d",&n,&m);        //输出地图大小
        for(int i=1;i<=n;i++)       //行
            for(int j=1;j<=m;j++)   //列
            scanf("%d",&w[i][j]);   //输入
        now=1,past=0;               //这里的1与0没有特别含义,仅仅是用两个变量来表示两个序列,每次交换表示,注意是相邻两格交换
        f[past][0]=1;               //一开始轮廓线是0,方案数是1,因为全部格(其实没有格)都用线连完了
        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);//读出当前格的右插头BIT1与下插头BIT2
                    //由这里可知插头的状态更新是由左往右是由低往高位
                    if (!bit1&&!bit2){                                  //两个插头都是0
                        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;}                      //插头不全是0但本格不可走方案直接0
                    else if (bit1&&bit2) f[now][k]=f[past][k-(1<<(j-1))-(1<<j)];    //两个插头都是1则转移两个插头都不要
                    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;       //扫完一行,第M个插头是的最右边那格的右边,如果是1这状态就不存在,方案数清0
                        else f[now][k]=f[now][k>>1];//要传递到下一行上一轮的状态左移一位就是这一次的状态了
                        //扫到行末时打最竖的那个状态是在最右边,也就是最高位,所以要左移当前状态,就把最高位的打竖移到最低位,也就是下一行的最左端,同时M个上下插头不影响
                    }
                }
                swap(now,past);//进入下一格,注意,是下一格
            }
        printf("Case %d: There are %lld ways to eat the trees.\n",t,f[past][0]);//输出答案
    }
    return 0;
}
/*
Sample Input
2
6 3
1 1 1
1 0 1
1 1 1
1 1 1
1 0 1
1 1 1
2 4
1 1 1 1
1 1 1 1
Sample Output
Case 1: There are 3 ways to eat the trees.
Case 2: There are 2 ways to eat the trees.

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值