基于连通性的状压dp

巧妙之处:插头已经可以表示内部所有状态了。

就是讨论麻烦一些。

简介

转移方法:
逐格转移,分类讨论

记录状态方法:
最小表示法(每次要重新编号,对于一类没用“回路路径”之类的题,可以胜任)

括号表示法(便于操作,但是一些题不能记录状态)

状态存储方法:

不能直接循环所有可能状态,因为状态不满太浪费

哈希+滚动数组

(clear时候,直接memset(hd),cnt=0就是最快的!!!!)

 

然后具体题目分清楚转移情况讨论即可。

例题

尝试加入各种剪枝以减少状态量。

经典入门例题:HDU 1693

Eat the Trees

 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=11;
int n,m;
int mp[N+2][N+2];
int T;
ll f[N+2][N+2][1<<(N+1)];
ll ans;
void wrk(){
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int s=0;s<(1<<m+1);s++){
                if(f[i][j][s]==0) continue;
                
                 if(j!=m){// not a end
                     //cout<<" here "<<i<<" "<<j<<" "<<s<<endl;
                    int le=(s>>j-1)&1;
                    int up=(s>>j)&1;
                    if(mp[i][j]==0){
                        if(le==0&&up==0){
                            f[i][j+1][s]+=f[i][j][s];
                        }
                    }
                    else{
                        int now=s;
                        if(le==1&&up==0){
                            int s0=s^(1<<j-1);
                            s0|=(1<<j);
                            f[i][j+1][s0]+=f[i][j][s];
                            f[i][j+1][s]+=f[i][j][s];
                        }
                        else if(le==1&&up==1){
                            int s0=s^(1<<j-1);
                            s0^=(1<<j);
                            f[i][j+1][s0]+=f[i][j][s];
                        }
                        else if(le==0&&up==1){
                            int s0=s^(1<<j);
                            s0|=(1<<j-1);
                            f[i][j+1][s0]+=f[i][j][s];
                            f[i][j+1][s]+=f[i][j][s];
                        }
                        else{
                            int s0=s|(1<<j);
                            s0|=(1<<j-1);
                            f[i][j+1][s0]+=f[i][j][s];
                        }
                    }
                }
                  else{// j==m 
                      int le=(s>>j-1)&1;
                    int up=(s>>j)&1;
                    if(mp[i][j]==0){
                        if(le==0&&up==0){
                            f[i+1][1][(s<<1)]+=f[i][j][s];
                        }
                    }
                    else{
                        int now=s;
                        //cout<<" irhf "<<i<<" "<<j<<" : "<<s<<" "<<le<<" "<<up<<endl;
                        if(le==1&&up==0){
        
                            f[i+1][1][s<<1]+=f[i][j][s];
                        }
                        else if(le==1&&up==1){
                            int s0=s^(1<<j-1);
                            s0^=(1<<j);
                            f[i+1][1][s0<<1]+=f[i][j][s];
                        }
                        else if(le==0&&up==1){
                            int s0=s^(1<<j);
                            s0|=(1<<j-1);
                            f[i+1][1][s0<<1]+=f[i][j][s];
                        }
                        else{
                            continue;
                        }
                    }
                }    
            }
        }
    }
}
int main()
{
    scanf("%d",&T);
    for(int o=1;o<=T;o++){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&mp[i][j]);
            }
        }
        ans=0;
        memset(f,0,sizeof f);
        f[1][1][0]=1;
        wrk();
        /*for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++){
             cout<<i<<" and "<<j<<" : "<<endl;
             for(int s=0;s<=(1<<m+1);s++){
                 cout<<" "<<s<<" |: "<<f[i][j][s]<<endl;
            }
         }*/
        
        ans=f[n+1][1][0];
        printf("Case %d: There are %lld ways to eat the trees.\n",o,ans);
    }
    return 0;
}
Eat the Trees

相关文章: