poj 1185 炮兵阵地 (状态压缩dp)

举报
xindoo 发表于 2022/04/16 02:07:43 2022/04/16
【摘要】 题目链接      如果你是刚刚开始做状态压缩dp,我建议你先看看 poj 3254 Corn Fields 这是一道比这一题更简单,更容易入门的题目。     还有在代码中我用了一个很巧妙的方法求一个数二进制数中1的个数  具体请看我博客中  x& (x...

题目链接

     如果你是刚刚开始做状态压缩dp,我建议你先看看 poj 3254 Corn Fields 这是一道比这一题更简单,更容易入门的题目。
    还有在代码中我用了一个很巧妙的方法求一个数二进制数中1的个数  具体请看我博客中  x& (x - 1)==0 这篇文章  链接  。
     还有一点,不同于poj 3254的地方,我们不能直接枚举所有的状态。我在getresult()中用到了四重循环,直接枚举的时间复杂度是2^40,并且dp那个数组也是开不下到,不过对于这道题还是有方法的。枚举一行所有的状态,行合法(没有两个1相隔少于两个)的状态总共有61中,我们只需要枚举所有合法状态即可,循环次数最多是61^4。

代码


   
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <algorithm>
  4. using namespace std;
  5. const int maxn = 62; //每行合法的状态总共有61种
  6. int dp[102][maxn][maxn]; //因为状态是与前两行有关,所以用到三维数组
  7. int sta[102]; //每行不能放置的点
  8. int cnt[maxn]; //状态中1的数目,也就是炮的数目
  9. int allsta[maxn]; //所有可能的状态
  10. int n, m;
  11. int num; //满足条件的状态数
  12. bool judgeself(int x) //判断x状态是不是有会相互攻击的状态,也就是有没有两个1相隔少于两个0
  13. {
  14. if (x&(x<<1) || x&(x<<2))
  15. return false;
  16. return true;
  17. }
  18. int count(int x) //用很巧妙的计算出x二进制中1的个数
  19. {
  20. int s = 0;
  21. while (x)
  22. {
  23. s++;
  24. x = x&(x-1);
  25. }
  26. return s;
  27. }
  28. void search(int x)
  29. { //这个函数相当于把对状态进行了离散化,去掉了每行所有不合法的状态
  30. int t = 1<<x;
  31. num = 0;
  32. for (int i = 0; i < t; i++)
  33. {
  34. if (judgeself(i))
  35. {
  36. allsta[num] = i;
  37. cnt[num] = count(i);
  38. num++;
  39. }
  40. }
  41. }
  42. bool fit(int x, int y)
  43. {
  44. if(x&y)
  45. return false;
  46. return true;
  47. }
  48. void getresult()
  49. {
  50. memset(dp, 0, sizeof(dp));
  51. for (int i = 0; i < num; i++) //因为计算每行的时候需要用到前两行的状态,需要对第一行特殊处理
  52. {
  53. for (int j = 0; j < num; j++)
  54. {
  55. if (fit(sta[1], allsta[i]) && fit(allsta[i], allsta[j]))
  56. dp[1][j][i] = cnt[i];
  57. }
  58. }
  59. for (int i = 2; i <= n; i++)
  60. {
  61. for (int j = 0; j < num; j++)
  62. {
  63. if (!fit(sta[i], allsta[j]))
  64. continue;
  65. for (int k = 0; k < num; k++)
  66. {
  67. if (!fit(allsta[j], allsta[k])) // 判断是否与前一行冲突
  68. continue;
  69. for (int l = 0; l < num; l++)
  70. {
  71. if (!fit(allsta[j], allsta[l])) //不光要判断是否与前一行冲突,要判断第前两行
  72. continue;
  73. dp[i][k][j] = max(dp[i][k][j], dp[i-1][l][k]+cnt[j]);
  74. }
  75. }
  76. }
  77. }
  78. int ans = 0;
  79. for (int i = 0; i < num; i++)
  80. {
  81. for (int j = 0; j < num; j++)
  82. {
  83. if (!fit(allsta[i], allsta[j]))
  84. continue;
  85. ans = max(ans, dp[n][i][j]); //求出最后一行最大的结果
  86. }
  87. }
  88. printf("%d\n", ans);
  89. }
  90. int main()
  91. {
  92. while (scanf("%d %d", &n, &m) != EOF)
  93. {
  94. memset(sta, 0, sizeof(sta));
  95. search(m);
  96. for (int i = 1; i <= n; i++)
  97. {
  98. getchar();
  99. for (int j = 1; j <= m; j++)
  100. {
  101. char t;
  102. scanf("%c", &t);
  103. if (t == 'H')
  104. sta[i] += (1<<(m-j)); //标记一行中不能安放炮兵的状态
  105. }
  106. }
  107. getresult();
  108. }
  109. return 0;
  110. }


文章来源: xindoo.blog.csdn.net,作者:xindoo,版权归原作者所有,如需转载,请联系作者。

原文链接:xindoo.blog.csdn.net/article/details/8902190

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。