动态规划--状态压缩

https://www.acwing.com/problem/content/293/

给定一个空矩阵,问用1*2的矩形将其填满存在多少种方案。

可以只考虑一种,如果横的都已经确定了,那么竖的只有0或者1种放法,所以只考虑横的就好了(只考虑竖的也行)

暴搜复杂度为O(N! * M!),所以只能考虑动态规划,用一个整数表示一列的状态。

 

朴素写法:

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 const int N=12,M=1<<12;
 5 long long f[N][M];
 6 bool st[M];
 7 int main(void){
 8     int n,m;
 9     while(cin>>n>>m,n||m){
10         for(int i=0;i< 1<<n;i++){
11             int cnt=0;
12             st[i]=true;
13             for(int j=0;j<n;j++){
14                 if(i>>j&1){
15                     if(cnt&1){
16                         st[i]=false;
17                     cnt=0;
18                     }
19                 }else{
20                     cnt++;
21                 }
22             }
23             if(cnt&1){
24                 st[i]=false;
25             }
26         }//预处理全部的状态。
27         memset(f,0,sizeof f);
28         f[0][0]=1;
29         for(int i=1;i<=m;i++){
30             for(int j=0;j<1<<n;j++){
31                 for(int k=0;k<1<<n;k++){
32                     if((j&k)==0&&st[j|k]){
33                         f[i][j]+=f[i-1][k];
34                     }
35                 }
36             }
37         }
38         cout<<f[m][0]<<endl;
39         
40     }
41     return 0;
42 }

 

 预处理好j对应的状态

 1 #include<iostream>
 2 #include<cstring>
 3 #include<vector>
 4 using namespace std;
 5 const int N=12,M=1<<12;
 6 long long f[N][M];
 7 bool st[M];
 8 vector<int> state[M];
 9 int main(void){
10     int n,m;
11     while(cin>>n>>m,n||m){
12         for(int i=0;i< 1<<n;i++){
13             int cnt=0;
14             st[i]=true;
15             for(int j=0;j<n;j++){
16                 if(i>>j&1){
17                     if(cnt&1){
18                         st[i]=false;
19                     cnt=0;
20                     }
21                 }else{
22                     cnt++;
23                 }
24             }
25             if(cnt&1){
26                 st[i]=false;
27             }
28         }//预处理全部的状态。
29         for(int j=0;j < 1<<n;j++){
30             state[j].clear();
31             for(int k=0;k< 1<<n;k++){
32                 if((j&k)==0&&st[j|k]){
33                     state[j].push_back(k);
34                 }
35             }
36         }
37         memset(f,0,sizeof f);
38         f[0][0]=1;
39         for(int i=1;i<=m;i++){
40             for(int j=0;j<1<<n;j++){
41                 for(auto k:state[j]){
42                     f[i][j]+=f[i-1][k];
43                 }
44             }
45         }
46         cout<<f[m][0]<<endl;
47         
48     }
49     return 0;
50 }

 

 

https://www.acwing.com/problem/content/93/

最短Hamilton路径,dfs写不了,因为复杂度为O(n!)。

只能用dp。

 

 

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 const int N=20;
 5 int w[N][N];
 6 int f[1<<N][N];
 7 int main(void){
 8     int n;
 9     cin>>n;
10     for(int i=0;i<n;i++){
11         for(int j=0;j<n;j++){
12             cin>>w[i][j];
13         }
14     }
15     memset(f,0x3f,sizeof f);
16     f[1][0]=0;
17     //先枚举状态是因为后面需要用到前面的状态
18     for(int i=0;i<1<<n;i++){//枚举状态
19         for(int j=0;j<n;j++){//枚举每一个点,如果i中有这个点,就枚举倒数第二个点
20             if((1<<j)&i){
21                 for(int k=0;k<n;k++){
22                     if((1<<k)&i){
23                         f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j]);
24                     }
25                 }
26             }
27         }
28     }
29     cout<<f[(1<<n)-1][n-1];
30     return 0;
31 }

 

posted on 2021-03-26 09:47  greenofyu  阅读(65)  评论(0编辑  收藏  举报