• 状压dp


    状压dp:用二进制压缩集合状态进行转移。

    TSP问题:

    91. 最短Hamilton路径

    给定一张完全图,求从0出发,周游所有点的最短曼哈顿距离,

    定义dp【i】【s】为从0点出发,走到 i 为终点,经过的集合为S的最短距离,

    那么初始化dp【0】【1】=0;直接转移,那么答案为dp【n-1】【1<<n -1】

    复杂度O(n*n*2^n)

    #include<bits/stdc++.h>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=1e6+500;
    int n,m;
    int gp[25][25],dp[25][N];
    int main(){
            scanf("%d",&n);
            for(int i=0;i<n;i++){
                for(int j=0;j<n;j++){
                    scanf("%d",&gp[i][j]);
                }
            }
            memset(dp,inf,sizeof dp);
            // for(int i=0;i<n;i++)dp[i][1<<i]=0;
            dp[0][1]=0;
                for(int S=1;S<(1<<n);S++){
                    for(int u=0;u<n;u++){
                        if(!(S&(1<<u)))continue;
                        for(int v=0;v<n;v++){
                            if(v==u)continue;
                            if(S&(1<<v))dp[u][S]=min(dp[u][S],dp[v][S^(1<<u)]+gp[v][u]);
                        }
                    }
                }   
    
                cout<<dp[n-1][(1<<n)-1]<<endl;
                // system("pause");
            return 0;
    }
    View Code

    Travelling

     HDU - 3001 

    一张完全图,每个点可以走0,1,2次,求周游所有点的最小曼哈顿距离,那么用三进制压缩状态,定义dp【i】【S】为 i  为起点,经过的集合为S的最短距离,

    那么答案为所有的结果取小。预处理每一个状态对应每一位的情况,直接转移即可。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int inf=0x3f3f3f3f;
     4 const int N=6e4+500;
     5 int n,m;
     6 int gp[20][20];
     7 int bit[]={0,1,3,9,27,81,243,729,2187,6561,19683,59049};
     8 int path[N][20],dp[20][N];
     9 void init(){
    10         memset(gp,inf,sizeof gp);
    11         memset(dp,inf,sizeof dp);
    12         for(int i=0;i<N;i++){
    13             int t=i;
    14             for(int j=1;j<=n;j++){
    15                 path[i][j]=t%3;t/=3;
    16             }
    17         }
    18 
    19 }
    20 int main(){
    21     while(~scanf("%d %d",&n,&m)){
    22         init();
    23         while(m--){
    24             int u,v,w;scanf("%d %d %d",&u,&v,&w);
    25             if(w<gp[u][v])gp[u][v]=gp[v][u]=w;
    26         }
    27         int ans=inf;
    28         for(int i=0;i<=n;i++)dp[i][ bit[i] ]=0;
    29         for(int j=0;j<=59049;j++){
    30             bool flag=1;
    31             for(int i=1;i<=n;i++){
    32 
    33                 if(path[j][i]==0){flag=0;continue;}
    34                 for(int k=1;k<=n;k++){
    35                     if(k==i)continue;
    36                     int l=j-bit[i];
    37                     dp[i][j]=min(dp[i][j],dp[k][l]+gp[k][i]);
    38                 }
    39 
    40             }
    41 
    42             if(flag){
    43                 for(int i=1;i<=n;i++)ans=min(ans,dp[i][j]);
    44             }
    45         }
    46         // cout<<"ans :";
    47         printf("%d
    ",(ans==inf?-1:ans));
    48 
    49     }
    50     // system("pause");
    51         return 0;
    52 }
    View Code

    Victor and World

     HDU - 5418 

    从1开始,周游所有点回到1,求最下距离,

    注意边很多,点很少,先floyd跑最短路,定义dp【i】【s】为1为起点,i为终点,经过的集合为s的最短距离,

    那么答案即为 :dp[0][(1<<n)-1]    dp[i][(1<<n)-1]+gp[i][0]  取小,

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int inf=0x3f3f3f3f;
     4 const int N=6e5+500;
     5 int n,m;
     6 int gp[20][20],dp[20][N];
     7 void floyd(){
     8     for(int k=0;k<n;k++){
     9         for(int i=0;i<n;i++){
    10             for(int j=0;j<n;j++){
    11                 gp[i][j]=min(gp[i][j],gp[i][k]+gp[k][j]);
    12             }
    13         }
    14     }
    15 }
    16 void init(){
    17     memset(gp,inf,sizeof gp);
    18     memset(dp,inf,sizeof dp);
    19     for(int i=0;i<n;i++)gp[i][i]=0;
    20 }
    21 int main(){
    22     int t;scanf("%d",&t);
    23         while(t--){
    24             scanf("%d %d",&n,&m);
    25             init();
    26             while(m--){
    27                 int u,v,w;scanf("%d %d %d",&u,&v,&w);
    28                 u--;v--;
    29                 if(w<gp[u][v])gp[u][v]=gp[v][u]=w;
    30             }
    31             floyd();
    32             dp[0][1]=0;
    33             for(int S=1;S<(1<<n);S++){
    34                 for(int u=0;u<n;u++){
    35                     if(!(S&(1<<u)))continue;
    36                     for(int v=0;v<n;v++){
    37                         if(v==u)continue;
    38                     if(S&(1<<v))dp[u][S]=min(dp[u][S],dp[v][S^(1<<u)]+gp[v][u]);
    39                     }
    40                 }
    41             }
    42             int ans=dp[0][(1<<n)-1];
    43             for(int i=1;i<n;i++)ans=min(ans,dp[i][(1<<n)-1]+gp[i][0]);
    44             printf("%d
    ",ans);
    45         }
    46     // system("pause");
    47         return 0;
    48 }
    View Code

    Corn Fields

     POJ - 3254 

    一张网格图,种玉米,1之间不能相邻。问有多少种方案。

    定义dp【i】【s】为i行状态为s的方案数,那么只要和地图,前一行的状态不冲突即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=6e5+500;
    int n,m;
    int a[20][20],dp[20][N],num[20];
    // bool fit(int x,int k){
    
    // }
    const int MOD=100000000;
    int main(){
            scanf("%d %d",&n,&m);
            for(int i=1;i<=n;i++){
                num[i]=0;
                for(int j=1;j<=m;j++){
                    int x;scanf("%d",&x);
                    if(x==0)num[i]+=1<<(m-j);
                }
            }    
            memset(dp,0,sizeof dp);
            for(int s=0;s<(1<<m);s++){
                if(!(s&s<<1)&& !(s&num[1]) )dp[1][s]=1;
            }
    
            for(int i=2;i<=n;i++){
                for(int s=0;s<(1<<m);s++){
                    if((s&s<<1)||s&num[i])continue;
                    for(int t=0;t<(1<<m);t++){
                        if(!(t&t<<1) && !(t&num[i-1]) && !(s&t))dp[i][s]=(dp[i][s]+dp[i-1][t])%MOD;
                    }
                }
            }
            int ans=0;
            for(int s=0;s<(1<<m);s++)if(!(s&s<<1)&&!(s&num[n]))ans=(ans+dp[n][s])%MOD;
                ans%=MOD;
                cout<<ans<<endl;
                // system("pause");
            return 0;
    }
    View Code

    郑厂长系列故事——排兵布阵

     HDU - 4539 

    一个地图,每个士兵不能曼哈顿距离为2的地方不能有士兵,求能放的最大士兵数。

    本来设dp【i】【s】为枚举到 i 行,状态为 s 的方案数,但这样转移实际上还要考虑 i - 2 行的状态,判断是否冲突。

    不妨设 dp 【 i 】【 j 】【 k 】为 枚举到 i 行,i 状态为 j     i - 1行状态为 k  的方案数,直接转移取大即可。

    #include<bits/stdc++.h>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=6e3+500;
    int n,m;
    int dp[120][200][200],a[120][120];
    vector<int>v;
    void init(){
        v.clear();
        for(int s=0;s<(1<<m);s++){
            if(!(s&(s<<2))&&!(s&(s>>2)))v.push_back(s);
        }
    }
    int count(int i,int x){
        int sum=0;
        for(int j=m;j>=1;j--){
            if(x&1)sum+=a[i][j];
            x>>=1;
        }
        return sum;
    }
    int main(){
    
        while(~scanf("%d %d",&n,&m)){
           init();
           for(int i=1;i<=n;i++){
                for(int j=1;j<=m;j++){
                    scanf("%d",&a[i][j]);
                }
           }
           memset(dp,0,sizeof dp);
           int ans=0;
            for(int i=1;i<=n;i++){
                for(int j=0;j<v.size();j++){
                    for(int k=0;k<v.size();k++){
                        if(i==1){
                            dp[i][j][k]=count(i,v[j]);
                        }
                        else {
                            if(v[j]&(v[k]>>1)||v[j]&(v[k]<<1))continue;
                            int tmp=0;
                            for(int p=0;p<v.size();p++){
                                if(v[k]&(v[p]>>1)||v[k]&(v[p]<<1))continue;
    
                                if(v[p]&v[j])continue;
                                tmp=max(tmp,dp[i-1][k][p]);
                            }
                            dp[i][j][k]=tmp+count(i,v[j]);
    
                        }                
                        ans=max(ans,dp[i][j][k]);
                    }
                }
            }        
            cout<<ans<<endl;
        }
            // system("pause");
            return 0;
    }
    View Code

    Mondriaan's Dream

     POJ - 2411 

    设dp[ i ] [ j ]为推到 i 行,状态为j,1表示是竖着放的上半块,0表示其他,那么只要保证1下面都是0,而且j | k不出现连续的奇数0,因为横着放达不到,直接递推即可。

    #include<iostream>
    #include<cstring>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=1e5+500;
    typedef long long ll;
    ll dp[20][1<<12];
    bool ins[1<<12];
    int main(){
        int n,m;
        while(cin>>n>>m&&n){
            memset(dp,0,sizeof dp);
            memset(ins,1,sizeof ins);
            for(int s=0;s<(1<<m);s++){
                int cnt=0,odd=0;
                for(int j=0;j<m;j++){
                    if((s>>j)&1)odd=odd|cnt,cnt=0;
                    else cnt^=1;
                }
                odd=odd|cnt;
                if(odd)ins[s]=0;
            }
            dp[0][0]=1;
            for(int i=1;i<=n;i++){
                for(int s=0;s<(1<<m);s++){
                    for(int k=0;k<(1<<m);k++){
                        if(!(s&k)&&ins[s|k])dp[i][s]+=dp[i-1][k];
                    }
                }
            }
            // cout<<"test :";
            cout<<dp[n][0]<<endl;
        }
            // system("pause");
            return 0;
    }
    View Code

    变滚动

    #include<iostream>
    #include<cstring>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=1e5+500;
    typedef long long ll;
    ll dp[2][1<<12];
    bool ins[1<<12];
    int main(){
        int n,m;
        while(cin>>n>>m&&n){
            memset(dp,0,sizeof dp);
            memset(ins,1,sizeof ins);
            for(int s=0;s<(1<<m);s++){
                int cnt=0,odd=0;
                for(int j=0;j<m;j++){
                    if((s>>j)&1)odd=odd|cnt,cnt=0;
                    else cnt^=1;
                }
                odd=odd|cnt;
                if(odd)ins[s]=0;
            }
            int old=0,now=1;
            dp[old][0]=1;
    
            for(int i=1;i<=n;i++){
                for(int s=0;s<(1<<m);s++){
                    dp[now][s]=0;
                    for(int k=0;k<(1<<m);k++){
                        if(!(s&k)&&ins[s|k])dp[now][s]+=dp[old][k];
                    }
                }
                for(int j=0;j<(1<<m);j++)dp[old][j]=dp[now][j];
    
            }
            cout<<dp[1][0]<<endl;
        }
            // system("pause");
            return 0;
    }
    View Code

     

    炮兵阵地

     POJ - 1185 

    #include<iostream>
    #include<cstring>
    #include<vector>
    // #include<bits/stdc++.h>
    
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=1e5+500;
    typedef long long ll;
    int dp[120][120][120];
    char mp[120][120];
    vector<int>v;
    int n,m;
    
    int get(int x,int y){
        int cnt=0;
        for(int j=1;j<=m;j++){
            if(mp[x][j]=='P'&&(y>>(m-j))&1)cnt++;
        }
        return cnt;
    }
    int main(){
            cin>>n>>m;
            for(int s=0;s<(1<<m);s++)if( !( (s<<2)&s||(s>>2)&s|| (s<<1)&s||(s>>1)&s) )v.push_back(s);
            memset(dp,0,sizeof dp);
            for(int i=1;i<=n;i++){
                for(int j=1;j<=m;j++){
                    cin>>mp[i][j];
                }
            }
            int ans=0;
            for(int i=1;i<=n;i++){
                for(int j=0;j<v.size();j++){
                    for(int k=0;k<v.size();k++){
                        if(v[j]&v[k])continue;
                        for(int s=0;s<v.size();s++){
                            if(v[j]&v[s]||v[k]&v[s])continue;
                            dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][s]+get(i,v[j]));
                        }
                        ans=max(ans,dp[i][j][k]);
                    }
                }
            }
            cout<<ans<<endl;
            // system("pause");
            return 0;
    }
    // 5 4
    // PHPP
    // PPHH
    // PPPP
    // PHPP
    // PHHP
    View Code
  • 相关阅读:
    【LeetCode 104_二叉树_遍历】Maximum Depth of Binary Tree
    【LeetCode 110_二叉树_遍历】Balanced Binary Tree
    【LeetCode 111_二叉树_遍历】Minimum Depth of Binary Tree
    【剑指Offer】36两个链表的第一个公共结点
    【剑指Offer】34第一个只出现一次的字符
    【剑指Offer】33丑数
    【剑指Offer】32把数组排成最小的数
    xgboost的原理没你想像的那么难(转载)
    【剑指Offer】31整数中1出现的次数(从1到n整数中1出现的次数)
    【剑指Offer】28连续子数组的最大和
  • 原文地址:https://www.cnblogs.com/littlerita/p/13383712.html
Copyright © 2020-2023  润新知