状态压缩DP总结

 

POJ1185 炮兵部队问题:

在平原上才能放置炮兵,每个炮兵的上下左右2格之内都不能出现别的炮兵

可以考虑在当前行放置炮兵它的右侧和下侧绝对不会出现炮兵即可,左侧和上侧就能省去考虑

明显的状态压缩dp题,但是题目所给的有10列,因为每行都与前两行的状态有关,那么也就是根据当前,上一行,上上行3行状态来修改

dp[i][v][u]的状态方程

因为这里直接3重循环会爆,但是我们很容易发现,可以预处理一些关于行的合法状态,那么状态数就少了很多,接下来考虑的时候就省去了行上的相关影响

 1 #include <iostream>

 2 #include <cstdio>

 3 #include <cstring>

 4 using namespace std;

 5 

 6 const int N = 1<<11;

 7 int dp[105][100][100] , state[N];

 8 int can[100] , tot[100] , cnt;

 9 char str[15];

10 

11 void init_dfs(int k , int u , int v)

12 {

13     if(k<0){

14         tot[cnt] = v;

15         can[cnt++] = u;

16         return;

17     }

18     init_dfs(k-3 , u|(1<<k) , v+1);

19     init_dfs(k-1 , u , v);

20 }

21 

22 int main()

23 {

24  //   freopen("a.in" , "r" , stdin);

25     int n,m;

26     while(scanf("%d%d" , &n , &m) != EOF)

27     {

28         for(int i=1 ; i<=n ; i++){

29             scanf("%s" , str);

30             state[i] = 0;

31             for(int j=0 ; j<(int)strlen(str) ; j++){

32                 state[i] <<= 1;

33                 if(str[j] == 'P') state[i]+=1;

34             }

35            // cout<<"state: "<<state[i]<<endl;

36         }

37         //找到所有符合的状态,减少状态数

38         cnt = 0;

39         init_dfs(m-1 , 0 , 0);

40      //   cout<<cnt<<endl;

41 

42         if(n==1){

43             int maxn=0;

44             for(int i=0 ; i<cnt ; i++)

45                 if((state[1]&can[i]) == can[i])

46                     maxn = max(maxn , tot[i]);

47             printf("%d\n" , maxn);

48             continue;

49         }

50 

51         memset(dp , 0 , sizeof(dp));

52         for(int i=0 ; i<cnt ; i++){

53             for(int j=0 ; j<cnt ; j++){

54                 int t1 = state[1]&can[i] , t2 = state[2]&can[j];

55                 if(t1 == can[i] && t2 == can[j] && !(can[i]&can[j]))

56                 {

57                    // cout<<"test: "<<i<<" "<<j<<" "<<can[i]<<" "<<can[j]<<" tot: "<<tot[i]<<" "<<tot[j]<<endl;

58                     dp[2][i][j] = tot[i] + tot[j];

59                 }

60             }

61         }

62         state[0] = (1<<m)-1;

63         for(int i=3 ; i<=n ; i++){

64             for(int u=0 ; u<cnt ; u++){

65                 if((state[i]&can[u]) < can[u]) continue;

66                 for(int v=0 ; v<cnt ; v++){

67                     if((state[i-1]&can[v]) < can[v]) continue;

68                     if((can[v]&can[u])) continue;

69                     for(int w=0 ; w<cnt ; w++){

70                         if((state[i-2]&can[w]) < can[w]) continue;

71                         if((can[v]&can[w]) || (can[u]&can[w])) continue;

72                         dp[i][v][u] = max(dp[i][v][u] , dp[i-1][w][v] + tot[u]);

73                     }

74                 }

75             }

76         }

77         int maxn = 0;

78         for(int i=0 ; i<cnt ; i++){

79             for(int j=0 ; j<cnt ; j++)

80                 maxn = max(maxn , dp[n][i][j]);

81         }

82         printf("%d\n" , maxn);

83     }

84     return 0;

85 }
View Code

POJ 2411 木板拼接问题:

在n*m的方格中,放1*2或者2*1的木板,问有多少摆放的种数

题目会超int,答案输出用long long

这里用dp[i][u]表示到达第 i 行时状态为 u ,且前面行的格子已经全部被木板填充完全,所能达到的方法种数

每次对于当前行的状态只跟上一行有关,dp[i+1][u] += dp[i][v] 

通过v可以拼出 u 的情况,我们总是在u,v上放2*1的板,或只在u 上放1*2的板,不能在v上放1*2的板,这样会与原来 v 作为当前行时在其上放1*2的情况重复

且必须保证全部放完后,上一行v 全部填充满(v == (1<<m)-1) ,才更新数据

 1 #include <iostream>

 2 #include <cstdio>

 3 #include <cstring>

 4 using namespace std;

 5 #define ll long long

 6 ll dp[15][10005];

 7 

 8 void dfs(int i , int u , int v , int full , int k , ll val)

 9 {

10     if(k<0){

11         if(v == full) dp[i][u] += val;

12         return ;

13     }

14     if(k>=1 && !(u&(3<<(k-1)))) dfs(i , u|(3<<(k-1)) , v , full ,k-2 , val);

15     if(!(u&(1<<k)) && !(v&(1<<k))) dfs(i , u|(1<<k) , v|(1<<k) , full , k-1 , val);

16     if(!(v&(1<<k))) return ;

17     dfs(i , u , v , full , k-1 , val);

18 }

19 

20 int main()

21 {

22   //  freopen("a.in" , "r" , stdin);

23     int n,m;

24     while(scanf("%d%d" , &n , &m) ,n||m)

25     {

26         memset(dp , 0 , sizeof(dp));

27         dfs(1 , 0 , (1<<m)-1 , (1<<m)-1 , m-1 , 1);

28         for(int i=2 ; i<=n ; i++){

29             for(int j=0 ; j<(1<<m) ; j++){

30                 if(dp[i-1][j]) dfs(i , 0 , j , (1<<m)-1 , m-1 , dp[i-1][j]);

31             }

32         }

33         printf("%I64d\n" , dp[n][(1<<m)-1]);

34     }

35     return 0;

36 }
View Code

HDU2280 填充问题:

将所给的积木装入平面上,保证最后剩余的格数能够尽可能的少

因为积木最大也就两行,所以每次当前行只受上一行的影响

dp[i][u]记录当前 i 行 状态 为 u 时剩余的格子的最小数目

  1 #include <cstdio>

  2 #include <cstring>

  3 #include <iostream>

  4 using namespace std;

  5 const int INF = 0x3f3f3f3f;

  6 const int N = 1005;

  7 

  8 int dp[N][40] , state[N];

  9 char str[8];

 10 

 11 int cnt_0(int u)

 12 {

 13     int ans = 5;

 14     while(u){

 15         if(u&1) ans--;

 16         u>>=1;

 17     }

 18     return ans;

 19 }

 20 

 21 void dfs1(int i , int u , int cnt , int k) //k为当前的长度,从0开始

 22 {

 23     if(k < 0){

 24         dp[i][u] = min(dp[i][u] , cnt);

 25         return ;

 26     }

 27     if(k >= 1 && !(u&(3<<(k-1)))){

 28         int v = u|(3<<(k-1));

 29         dfs1(i , v , cnt-2 , k-2);

 30     }

 31     dfs1(i,u,cnt,k-1);

 32 }

 33 //u为当前行状态,v为上一行状态,cnt为当前行0的数量

 34 void dfs2(int i , int u , int v , int cnt , int k)

 35 {

 36     if(k < 0){

 37         dp[i][u] = min(dp[i][u] , cnt);

 38         return ;

 39     }

 40     if(k>=1){

 41         if(!(u&(3<<(k-1)))){

 42             int p = u|(3<<(k-1));

 43             dfs2(i , p , v , cnt-2 , k-2);

 44         }

 45         if(!(u&(3<<(k-1))) && !(v&(1<<(k-1)))){

 46             int p = u|(3<<(k-1));

 47             int q = v|(1<<(k-1));

 48             dfs2(i , p , q , cnt-3 , k-2);

 49         }

 50         if(!(u&(3<<(k-1))) && !(v&(1<<k))){

 51             int p = u|(3<<(k-1));

 52             int q = v|(1<<k);

 53             dfs2(i , p , q , cnt-3 , k-2);

 54         }

 55         if(!(v&(3<<(k-1))) && !(u&(1<<(k-1)))){

 56             int p = u|(1<<(k-1));

 57             int q = v|3<<(k-1);

 58             dfs2(i , p , q , cnt-3 , k-2);

 59         }

 60         if(!(v&(3<<(k-1))) && !(u&(1<<k))){

 61             int p = u|(1<<k);

 62             int q = v|(3<<(k-1));

 63             dfs2(i , p , q , cnt-3 , k-1);

 64         }

 65     }

 66     if(!(u&(1<<k)) && !(v&(1<<k))){

 67         int p = u|(1<<k);

 68         int q = v|(1<<k);

 69         dfs2(i , p , q , cnt-2 , k-1);

 70     }

 71     dfs2(i , u , v , cnt , k-1);

 72 }

 73 

 74 int main()

 75 {

 76   //  freopen("a.in" , "r" , stdin);

 77     int n , m;

 78     while(scanf("%d%d" , &n , &m) != EOF)

 79     {

 80         for(int i=1 ; i<=n ; i++){

 81             state[i] = 0;

 82             scanf("%s" , str);

 83             for(int j=0 ; j<5 ; j++){

 84                 int t = str[j]-'0';

 85                 state[i] = state[i]*2+t;

 86             }

 87         }

 88 

 89         memset(dp , 0x3f , sizeof(dp));

 90         dp[1][state[1]] = cnt_0(state[1]);

 91         dfs1(1 , state[1] , dp[1][state[1]] , 4);

 92 

 93         for(int i=2 ; i<=n ; i++){

 94             for(int p=0 ; p<32 ; p++){

 95                 if(dp[i-1][p] <INF){

 96                   //  cout<<"state : i: "<<i<<" p: "<<p<<" "<<dp[i-1][p]+cnt_0(state[i])<<endl;

 97                     dfs2(i , state[i] , p , dp[i-1][p]+cnt_0(state[i]) , 4);

 98                 }

 99             }

100         }

101         int minn = INF;

102         for(int i=0 ; i<32 ; i++)

103             minn = min(minn , dp[n][i]);

104      //   cout<<minn<<endl;

105         printf("%s\n" , minn<=m?"YES":"NO");

106     }

107     return 0;

108 }
View Code

 HDU 2442 方格填充

通过所给的5种形式的木块无覆盖填充n*m的方格,问最多有多少方格被填充

 1 #include <iostream>

 2 #include <cstdio>

 3 #include <cstring>

 4 

 5 using namespace std;

 6 

 7 const int N = 1<<6;

 8 int dp[105][N][N];

 9 

10 void dfs(int i , int w , int v , int u , int val , int k)

11 {

12     if(k<0){

13         dp[i][v][u] = max(dp[i][v][u] , val);

14         return;

15     }

16     if(k>=2){

17         if(!(w&(1<<(k-1))) && !(v&(7<<(k-2))) && !(u&(1<<(k-1))))

18             dfs(i , w|(1<<(k-1)) , v|(7<<(k-2)) , u|(1<<(k-1)) , val+5 , k-3);

19         if(!(v&(7<<(k-2))) && !(u&(1<<(k-1))))

20             dfs(i , w , v|(7<<(k-2)) , u|(1<<(k-1)) , val+4 , k-3);

21         if(!(v&(7<<(k-2))) && !(u&(1<<k)))

22             dfs(i , w , v|(7<<(k-2)) , u|(1<<k) , val+4 , k-3);

23     }

24     if(k>=1){

25         if(!(w&(1<<k)) && !(v&(3<<(k-1))) && !(u&(1<<k)))

26             dfs(i , w|(1<<k) , v|(3<<(k-1)) , u|(1<<k) , val+4 , k-2);

27         if(!(w&(3<<(k-1))) && !(v&(1<<k)) && !(u&(1<<k)))

28             dfs(i , w|(3<<(k-1)) , v|(1<<k) , u|(1<<k) , val+4 , k-1);

29     }

30     dfs(i , w , v , u , val , k-1);

31 }

32 

33 int main()

34 {

35   //  freopen("a.in" , "r" , stdin);

36     int n,m;

37     while(scanf("%d%d" , &n , &m) == 2)

38     {

39         memset(dp , -1 , sizeof(dp));

40         dp[1][(1<<m)-1][0] = 0;

41         for(int i=2 ; i<=n ; i++){

42             for(int w=0 ; w<(1<<m) ; w++){

43                 for(int v=0 ; v<(1<<m) ; v++){

44                     if(dp[i-1][w][v]>=0) dfs(i , w , v , 0 , dp[i-1][w][v] , m-1);

45                 }

46             }

47         }

48         int ans = 0;

49         for(int v=0 ; v<(1<<m) ; v++)

50             for(int u=0 ; u<(1<<m) ; u++)

51                 ans = max(ans , dp[n][v][u]);

52         printf("%d\n" , ans);

53     }

54     return 0;

55 }
View Code

 

你可能感兴趣的:(总结)