状压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
原文地址:https://www.cnblogs.com/littlerita/p/13383712.html