hdu4539排兵布阵(压状dp)
题目描述:
郑厂长系列故事——排兵布阵
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 4283 Accepted Submission(s): 1412
Problem Description
郑厂长不是正厂长
也不是副厂长
他根本就不是厂长
事实上
他是带兵打仗的团长
一天,郑厂长带着他的军队来到了一个n*m的平原准备布阵。
根据以往的战斗经验,每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因平原上也不是每一个位置都可以安排士兵。
现在,已知n,m 以及平原阵地的具体地形,请你帮助郑厂长计算该阵地,最多能安排多少个士兵。
也不是副厂长
他根本就不是厂长
事实上
他是带兵打仗的团长
一天,郑厂长带着他的军队来到了一个n*m的平原准备布阵。
根据以往的战斗经验,每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因平原上也不是每一个位置都可以安排士兵。
现在,已知n,m 以及平原阵地的具体地形,请你帮助郑厂长计算该阵地,最多能安排多少个士兵。
Input
输入包含多组测试数据;
每组数据的第一行包含2个整数n和m (n <= 100, m <= 10 ),之间用空格隔开;
接下来的n行,每行m个数,表示n*m的矩形阵地,其中1表示该位置可以安排士兵,0表示该地形不允许安排士兵。
每组数据的第一行包含2个整数n和m (n <= 100, m <= 10 ),之间用空格隔开;
接下来的n行,每行m个数,表示n*m的矩形阵地,其中1表示该位置可以安排士兵,0表示该地形不允许安排士兵。
Output
请为每组数据计算并输出最多能安排的士兵数量,每组数据输出一行。
Sample Input
6 6
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Sample Output
2
思路:标准的矩形压状套路题,dp[f][i][j],当前状态为i,前一状态为j的最大数量,需要注意的是行数大于了100,而如果再加一维就超内存了,此时需要滚动数组优化内存
AC代码:
#include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> #include<queue> #include<stack> #include<vector> #include<map> #include<set> #include<string> #include<math.h> using namespace std; #define inf 0x3f3f3f3f const int maxn = 20; struct node { int num, now; }; vector<node>book[105]; int n,m; int dic[maxn]; int maze[105][maxn]; bool vis[maxn]; void dfs(int id,int pos, int now, int num) { if (pos >= m) { book[id].push_back({ num,now }); return; } if ((!vis[pos])&&maze[id][pos]) {//当前位置能放,而且前两个格子的位置还没有放 vis[pos + 2] = 1; dfs(id,pos + 1, now + dic[pos], num + 1); vis[pos + 2] = 0; } dfs(id,pos + 1, now, num); } int ans; int dp[2][1024][1024];//dp[f][i][j],当前状态为i,前一状态为j的最大数量 int main() { //freopen("test.txt", "r", stdin); dic[0] = 1; for (int i = 1; i <= 10; i++) { dic[i] = dic[i - 1] << 1; } while (~scanf("%d%d", &n, &m)) { memset(dp, 0, sizeof(dp)); for (int i = 0; i < n; i++) { book[i].clear(); }//一定要注意初始化,我忘记了就想了好久... ans = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { scanf("%d", &maze[i][j]); } } for (int i = 0; i < n; i++) {//求出每一行的所有 dfs(i, 0, 0, 0); } if (n == 1) {//n==1的特殊情况特殊判断 for (int i = 0; i <book[0].size(); i++) { ans = max(ans, book[0][i].num); } printf("%d\n", ans); continue; } int f = 1; for (int i = 0; i < book[1].size(); i++) { for (int j = 0; j < book[0].size(); j++) { if (book[1][i].now & (book[0][j].now << 1))continue; if (book[1][i].now & (book[0][j].now >> 1))continue;//斜线上是曼哈顿距离为2的位置,不能取 int s = book[1][i].now, s1 = book[0][j].now; dp[f][s][s1] = book[0][j].num + book[1][i].num; } } for (int i = 2; i < n; ++i) { f ^= 1;//滚动数组 memset(dp[f], 0, sizeof(dp[f])); for (int j = 0; j < book[i].size(); j++) { int s = book[i][j].now; for (int k = 0; k <book[i-1].size(); k++) { int s1 = book[i - 1][k].now; dp[f][s][s1] = 0; if (s1 &(s << 1))continue; if (s1 & (s >> 1))continue; for (int h = 0; h < book[i-2].size(); h++) { int s2 = book[i - 2][h].now; if (s2 & s)continue;//垂直距离为2的位置曼哈顿距离也为2 if (s1 & (s2 << 1))continue; if (s1 & (s2 >> 1))continue; dp[f][s][s1] = max(dp[f][s][s1], dp[f^1][s1][s2] + book[i][j].num); } } } } for (int i = 0; i < book[n-1].size(); i++) { for (int j = 0; j < book[n-2].size(); j++) { int s = book[n-1][i].now, s1 = book[n-2][j].now; ans = max(ans, dp[f][s][s1]); } } printf("%d\n", ans); } return 0; }