DP_状态压缩DP

Poj 2411 Mondriaan's Dream (DP_状态压缩DP)

分类: 全部博客 ACM_好题经典题 ACM_动态规划(DP)   444人阅读  评论(1)  收藏  举报

题目链接:http://poj.org/problem?id=2411


题目大意: 给定一个n*m的方格矩形,求用1*2的小矩形完全覆盖的方案数,n <= 11,m <= 11.


解题思路: 挺经典的一类状态DP--子矩形覆盖父矩形,这类DP一般对一行的01状态进行压缩,然后按行进行转移。

    这题中每列的0表示未被覆盖,下一行的就必须覆盖它,我是理解成一个插头,留给下一行一个插头。每列的1表示已被覆盖,或者理解成没有向下的插头。因为当前行只会受上一行影响,所以可以一行一行进行转移,如果前一个状态能到下一个状态,那么就能转移。

    设dp[i][j]表示到第i行状态为j的方案数,那么dp[i][j] += dp[i][k] (if (Ok(j->k))).问题就变成Ok函数怎么写呢?易知0->1(上一行为0,当前行为1),那么1->?1->0肯定可以,上面无插头,下面留一个插头。1->1呢?不能单独判,必须判下一列是不是也是1->1,不是则不Ok。其实最早我不是这样写的,最早的写法很好理解,如果上一行为0那么当前行必须为1,如果有一排的1,那么当前行也必须有0*2或1*2或2*2个1与之对应,2个1必须连续,不过这样写跑了1000ms,而用现在这个Ok则跑了200ms。

    本题还有一个剪枝,当i * j为奇数的时候方案数为0,因为子矩形的方格数为2是偶数,没办法组成一个奇数。

    还有我的写法是从0行开始转移的,这样可以if(dp[i][j]) xxxxoooo进行剪枝

 

测试数据:

[cpp]  view plain copy
  1. f[1][1] = 0;  
  2. f[1][2] = 1;  
  3. f[1][3] = 0;  
  4. f[1][4] = 1;  
  5. f[1][5] = 0;  
  6. f[1][6] = 1;  
  7. f[1][7] = 0;  
  8. f[1][8] = 1;  
  9. f[1][9] = 0;  
  10. f[1][10] = 1;  
  11. f[1][11] = 0;  
  12. f[2][1] = 1;  
  13. f[2][2] = 2;  
  14. f[2][3] = 3;  
  15. f[2][4] = 5;  
  16. f[2][5] = 8;  
  17. f[2][6] = 13;  
  18. f[2][7] = 21;  
  19. f[2][8] = 34;  
  20. f[2][9] = 55;  
  21. f[2][10] = 89;  
  22. f[2][11] = 144;  
  23. f[3][1] = 0;  
  24. f[3][2] = 3;  
  25. f[3][3] = 0;  
  26. f[3][4] = 11;  
  27. f[3][5] = 0;  
  28. f[3][6] = 41;  
  29. f[3][7] = 0;  
  30. f[3][8] = 153;  
  31. f[3][9] = 0;  
  32. f[3][10] = 571;  
  33. f[3][11] = 0;  
  34. f[4][1] = 1;  
  35. f[4][2] = 5;  
  36. f[4][3] = 11;  
  37. f[4][4] = 36;  
  38. f[4][5] = 95;  
  39. f[4][6] = 281;  
  40. f[4][7] = 781;  
  41. f[4][8] = 2245;  
  42. f[4][9] = 6336;  
  43. f[4][10] = 18061;  
  44. f[4][11] = 51205;  
  45. f[5][1] = 0;  
  46. f[5][2] = 8;  
  47. f[5][3] = 0;  
  48. f[5][4] = 95;  
  49. f[5][5] = 0;  
  50. f[5][6] = 1183;  
  51. f[5][7] = 0;  
  52. f[5][8] = 14824;  
  53. f[5][9] = 0;  
  54. f[5][10] = 185921;  
  55. f[5][11] = 0;  
  56. f[6][1] = 1;  
  57. f[6][2] = 13;  
  58. f[6][3] = 41;  
  59. f[6][4] = 281;  
  60. f[6][5] = 1183;  
  61. f[6][6] = 6728;  
  62. f[6][7] = 31529;  
  63. f[6][8] = 167089;  
  64. f[6][9] = 817991;  
  65. f[6][10] = 4213133;  
  66. f[6][11] = 21001799;  
  67. f[7][1] = 0;  
  68. f[7][2] = 21;  
  69. f[7][3] = 0;  
  70. f[7][4] = 781;  
  71. f[7][5] = 0;  
  72. f[7][6] = 31529;  
  73. f[7][7] = 0;  
  74. f[7][8] = 1292697;  
  75. f[7][9] = 0;  
  76. f[7][10] = 53175517;  
  77. f[7][11] = 0;  
  78. f[8][1] = 1;  
  79. f[8][2] = 34;  
  80. f[8][3] = 153;  
  81. f[8][4] = 2245;  
  82. f[8][5] = 14824;  
  83. f[8][6] = 167089;  
  84. f[8][7] = 1292697;  
  85. f[8][8] = 12988816;  
  86. f[8][9] = 108435745;  
  87. f[8][10] = 1031151241;  
  88. f[8][11] = 8940739824;  
  89. f[9][1] = 0;  
  90. f[9][2] = 55;  
  91. f[9][3] = 0;  
  92. f[9][4] = 6336;  
  93. f[9][5] = 0;  
  94. f[9][6] = 817991;  
  95. f[9][7] = 0;  
  96. f[9][8] = 108435745;  
  97. f[9][9] = 0;  
  98. f[9][10] = 14479521761;  
  99. f[9][11] = 0;  
  100. f[10][1] = 1;  
  101. f[10][2] = 89;  
  102. f[10][3] = 571;  
  103. f[10][4] = 18061;  
  104. f[10][5] = 185921;  
  105. f[10][6] = 4213133;  
  106. f[10][7] = 53175517;  
  107. f[10][8] = 1031151241;  
  108. f[10][9] = 14479521761;  
  109. f[10][10] = 258584046368;  
  110. f[10][11] = 3852472573499;  
  111. f[11][1] = 0;  
  112. f[11][2] = 144;  
  113. f[11][3] = 0;  
  114. f[11][4] = 51205;  
  115. f[11][5] = 0;  
  116. f[11][6] = 21001799;  
  117. f[11][7] = 0;  
  118. f[11][8] = 8940739824;  
  119. f[11][9] = 0;  
  120. f[11][10] = 3852472573499;  
  121. f[11][11] = 0;  


代码:
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define MAX (1<<11)  
  4.   
  5.   
  6. long long dp[20][MAX];  
  7. int n,m,ha[20],hb[20];  
  8.   
  9.   
  10. int Ok(int pre,int cur) {  
  11.   
  12.     for (int i = 0; i < m; ++i) {  
  13.   
  14.         int t1 = pre & (1<<i);  
  15.         int t2 = cur & (1<<i);  
  16.         if (!t1 && !t2) return 0;  
  17.         if (t1 && t2) {  
  18.   
  19.             i++;  
  20.             if (i == m) return 0;  
  21.             if ((pre&(1<<i)) == 0) return 0;  
  22.             if ((cur&(1<<i)) == 0) return 0;  
  23.         }  
  24.     }  
  25.     return 1;  
  26. }  
  27. int main()  
  28. {  
  29.     int i,j,k,bigest;  
  30.   
  31.   
  32.     while (scanf("%d%d",&n,&m),n + m) {  
  33.   
  34.         if (m > n) k = m,m = n,n = k;  
  35.         if (n % 2 && m % 2) {  
  36.   
  37.             printf("0\n");  
  38.             continue;  
  39.         }  
  40.           
  41.   
  42.           
  43.         memset(dp,0,sizeof(dp));  
  44.         bigest = (1<<m) - 1;  
  45.         dp[0][bigest] = 1;  
  46.           
  47.           
  48.         for (i = 0; i < n; ++i)  
  49.            for (j = 0; j <= bigest; ++j)  
  50.                 if (dp[i][j]) for (k = 0; k <= bigest; ++k)  
  51.                     if (Ok(j,k)) dp[i+1][k] += dp[i][j];  
  52.         printf("%lld\n",dp[n][bigest]);  
  53.     }  
  54. }  

 

Poj 1170 Shopping Offers (DP_状态压缩DP)

分类: 全部博客 ACM_好题经典题   348人阅读  评论(1)  收藏  举报
题目链接: http://poj.org/problem?id=1170

题目大意给定一个订单n种商品,每种商品有个初始价格pri,初始数量numi,再给定m种组合,这些组合会得到便宜的价格。最后问怎么搭配使得总价格最少。


解题思路:搜状态压缩的题目时搜到关于这题的解题报告,名曰《经典状态压缩DPxxx》,然后我很认真地做了这题,还想好好吸收下,没想到是水题,囧。

    先将每种物品用一个六进制数表示,第1个物品表示为st1 = 1,第2个物品表示为st2 = 6....,这样订单上的物品总状态为nn = num1 * 1 + num2 * 6 ..

    这题其实是背包,n+m种物品,前n种物品体积为sti,价值为pri,后m种物品体积为组合内物品压缩成的一个状态stmi,价值为更便宜的那个价格。

    最后用完全背包来写就好。


测试数据:
Input:
2
7 3 2
8 2 5
2
1 7 3 5
2 7 1 8 2 10
OutPut
14


代码:
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define MAX 110000  
  4. #define INF (1<<29)  
  5. #define min(a,b) ((a)<(b)?(a):(b))  
  6.   
  7.   
  8. struct product {  
  9.       
  10.     int code,st,num,price;  
  11. }arr[MAX],special[MAX];  
  12. int dp[MAX],hash[1000],n,m,nn,ans;  
  13. int ST[10] = {1,6,36,216,1296,7776,46656,279936};  
  14.   
  15.   
  16. void Initial() {  
  17.   
  18.     ans = nn = 0;  
  19.     memset(hash,-1,sizeof(hash));  
  20.     for (int i = 0; i < ST[6]; ++i)  
  21.         dp[i] = INF;  
  22. }  
  23. void Solve_DP() {  
  24.   
  25.     int i,j,k;  
  26.     dp[0] = 0;  
  27.     for (i = 0; i <= nn; ++i)  
  28.         for (j = 0; j < n + m; ++j)  
  29.             dp[i+arr[j].st] = min(dp[i+arr[j].st],dp[i]+arr[j].price);  
  30.     ans = min(ans,dp[nn]);  
  31. }  
  32.   
  33.   
  34. int main()  
  35. {  
  36.     int i,j,k,tp,num,code;  
  37.   
  38.   
  39.     while (scanf("%d",&n) != EOF) {  
  40.   
  41.         Initial();  
  42.         for (i = 0; i < n; ++i) {  
  43.   
  44.             scanf("%d%d%d",&arr[i].code,&arr[i].num,&arr[i].price);  
  45.             arr[i].st = ST[i];  
  46.             hash[arr[i].code] = i;  
  47.             nn += arr[i].num * arr[i].st;  
  48.             ans += arr[i].num * arr[i].price;  
  49.         }  
  50.         scanf("%d",&m);  
  51.         for (i = 0; i < m; ++i) {  
  52.   
  53.             scanf("%d",&k);  
  54.             arr[i+n].st = 0;  
  55.             for (j = 0; j < k; ++j) {  
  56.   
  57.                 scanf("%d%d",&code,&num);  
  58.                 if (hash[code] == -1) continue;  
  59.                 arr[i+n].st += arr[hash[code]].st * num;  
  60.             }  
  61.             scanf("%d",&arr[i+n].price);  
  62.         }  
  63.   
  64.   
  65.         Solve_DP();  
  66.         printf("%d\n",ans);  
  67.     }  
  68. }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值