zoukankan      html  css  js  c++  java
  • [学习笔记]插头dp

    基于连通性的状压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.
    ",o,ans);
        }
        return 0;
    }
    Eat the Trees

    【模板】插头dp

    https://www.luogu.org/problemnew/show/P5056

    直接memset就是最好用的了。。。非要暴力清空结果TLE了一夜一页

    对于下面和右面有障碍的,就不要填出插头了。剪枝

    // luogu-judger-enable-o2
    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define il inline
    #define reg register int
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=20;
    const int M=600000;
    const int mo=1<<19;
    int n,m;
    char mp[N][N];
    int bit[N];
    int lasx,lasy;
    int num(int s,int k){//s's kth pos
        return (s>>bit[k-1])&3;
    }
    void zip(int s){
        for(reg i=1;i<=m+1;++i){
            cout<<num(s,i)<<" ";
        }cout<<endl;
    }
    struct hah{
        int val[M],hd[mo],nxt[M];
        ll dp[M];
        int cnt;
        void upda(int s,ll v){
            int p=s%mo;
    //        cout<<" upda ";zip(s);
    //        cout<<"hash "<<s<<" v "<<v<<" p "<<p<<" cnt "<<cnt<<" hd "<<hd[p]<<endl;
            for(reg i=hd[p];i;i=nxt[i]){
    //            cout<<"i "<<i<<" nxt "<<nxt[i]<<endl;
                if(val[i]==s){
    //                cout<<" fin "<<i<<endl;
                    dp[i]+=v;return;
                }
            }
    //        cout<<"build new "<<endl;
            ++cnt;nxt[cnt]=hd[p];dp[cnt]=v;val[cnt]=s;
            hd[p]=cnt;
        }
        void clear(){
            memset(hd,0,sizeof hd);cnt=0;
        }
    }f[2];
    
    int ad(int k,int c){
    //    cout<<" k "<<k<<" "<<c<<" bit "<<bit[k-1]<<endl;
        return (1<<(bit[k-1]))*c;
    }
    int get(int s,int k){
        int lp=num(s,k);
        if(lp==1){//after
            int cnt=0;
            for(reg i=k+1;i<=m+1;++i){
                if(num(s,i)==1) ++cnt;
                else if(num(s,i)==2) --cnt;
                if(cnt<0) return i;
            }
        }else{//before
            int cnt=0;
            for(reg i=k-1;i>=1;--i){
                if(num(s,i)==1) --cnt;
                else if(num(s,i)==2) ++cnt;
                if(cnt<0) return i;
            }
        }
        cout<<" bug "<<endl;
        return -233;//bug
    }
    
    ll wrk(){
        int tmp=0;
        f[tmp].upda(0,1);
        ll lim=(1<<(2*(m+1)))-1;
        for(reg i=1;i<=n;++i){
    //        cout<<" i "<<i<<endl;
            for(reg j=1;j<=f[tmp].cnt;++j) {
                f[tmp].val[j]=(f[tmp].val[j]*4)&lim;
    //            cout<<" num "<<j<<" : "<<" dp "<<f[tmp].dp[j]<<" zip ";zip(f[tmp].val[j]);
            }
            for(reg j=1;j<=m;++j){
    //            cout<<" j "<<j<<endl;
                tmp^=1;
                f[tmp].clear();
                int pr=tmp^1;
    //            cout<<" cnt "<<f[pr].cnt<<endl;
                for(reg k=1;k<=f[pr].cnt;++k){
    //                cout<<" k "<<k<<endl;
                    int s=f[pr].val[k];
    //                int u=num(s,j+1),l;//=num(s,j);
                    ll v=f[pr].dp[k];
    //                cout<<" v "<<v;cout<<" s ";zip(s);//endl;
                    if(mp[i][j]=='*'){
                        if(num(s,j)==0&&num(s,j+1)==0) f[tmp].upda(s,v);
                    }else{
                        if(num(s,j)==0&&num(s,j+1)==0){//new
                            if(j!=m)f[tmp].upda(s+ad(j,1)+ad(j+1,2),v);
                        }
                        else if(num(s,j)==0){//only up
    //                        cout<<" only up"<<endl;
                            f[tmp].upda(s+ad(j,num(s,j+1))+ad(j+1,-num(s,j+1)),v);
                            if(j!=m)f[tmp].upda(s,v);
                        }else if(num(s,j+1)==0){//only left
    //                        cout<<" only left "<<endl;
                            if(j!=m)f[tmp].upda(s+ad(j,-num(s,j))+ad(j+1,num(s,j)),v);
                            f[tmp].upda(s,v);
    //                        cout<<" after upda "<<endl;
                        }else{//both
    //                    cout<<" both "<<endl;
                            if(num(s,j)==1&&num(s,j+1)==1){
                                int to=s;to+=ad(j,-num(s,j))+ad(j+1,-num(s,j+1));
                                to+=ad(get(s,j+1),-1);
                                f[tmp].upda(to,v);
                            }else if(num(s,j)==2&&num(s,j+1)==2){
                                int to=s;to+=ad(j,-num(s,j))+ad(j+1,-num(s,j+1));
                                to+=ad(get(s,j),1);
                                f[tmp].upda(to,v);
                            }else if(num(s,j)==2&&num(s,j+1)==1){
                                int to=s;to+=ad(j,-num(s,j))+ad(j+1,-num(s,j+1));
                                f[tmp].upda(to,v);
                            }else{
                                if(i==lasx&&j==lasy){
                                    int to=s;to+=ad(j,-num(s,j))+ad(j+1,-num(s,j+1));
                                    f[tmp].upda(to,v);
                                }
                            }
                        }
                    }
                }
            }
        }
        for(reg i=1;i<=f[tmp].cnt;++i){
            if(f[tmp].val[i]==0) return f[tmp].dp[i];
        }
        return 0;
    }
    int main(){
        rd(n);rd(m);
        for(reg i=1;i<=19;++i) bit[i]=i<<1;
        for(reg i=1;i<=n;++i){
            scanf("%s",mp[i]+1);
        }
        for(reg i=1;i<=n;++i){
            for(reg j=1;j<=m;++j){
                if(mp[i][j]=='.') lasx=i,lasy=j;
            }
        }
        cout<<wrk();
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/3/31 21:44:47
    */
    View Code
  • 相关阅读:
    安装Windows Live Writer
    CSS实现鼠标滑过表格变色
    简单实用TAB选项卡,支持单页面多个调用
    在asp:Repeater中的label中分类绑定值时用asp:Repeater的ItemDataBound方法
    在asp:Repeater中的asp:LinkButton中按Id删除对应行的数据时用asp:Repeater的ItemCommand方法
    密码请设为616位字母或数字的检查
    List 和 IList的区别
    取得前九条之后的数据
    对List(IList)集合作求和,最大(小)值操作
    验证码验证
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9521010.html
Copyright © 2011-2022 走看看