ACM: 动态规划题 poj 1185 想了两…
2016-05-19 23:25
337 查看
[align=center]炮兵阵地[/align]
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H"
表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N
<= 100;M <= 10。
Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6
题意: 给定一块地形, 包括山丘'H'和平原'P'两种, 平地可以放置炮兵, 每个炮兵的攻击范围
是一个左右各2个格和上下各2个格. 要求炮兵相互不能攻击, 计算最大放置炮兵数.
(山地不可以放置, 平原可以).
解题思路:
1.
题意很清晰, 设状态: dp[i][j][k]表示: 当前第i层状态为j, 第i-1层状态为k,
从0层~i层的在当前状态的炮兵个数.(依然采用状态压缩方法)
2. 动态方程: dp[i][j][k] = (dp[i-1][k][t]+当前层(第i层)的炮兵数) (t是第i-2层状态)
一开始想用递归方法处理DP(d,
i, j, k, count, x, y)//d列,i当前层状态, j上一层状态
k是上一层的上一层状态, count计数炮兵, (x,y)坐标用来判断山地和平原.
DP(d, i, j, k, count, x, y)
{
if(d == m) //最后一列
dp[i][j][k] = (dp[i-1][k][t]+count);
return;
if(d+1 <= m) ....
if(d+3 <= m) ....
}
发现处理边界时候很复杂. 并且每行的状态2^10, 空间复杂度O(100*2^10*1^10)非常大.
这样处理不实际, 这里纠结了好久.
3.
接着想怎么可以将状态减少, 发现因为炮兵的横向攻击范围是左右各2个格, 所有一半的
状态时无效的,
空间复杂度降低到O(100*2^6*2*6). 既然每行可行的状态不多, 可以保存
行的可行解作为预处理(关键).
for(i = 0; i < (1<<m);
++i)
if( !( (i &
(i<<1)) || (i
& (i<<2))
) )
i就是一个可行解.
4. 有了预处理, 用递推方法就方便多了;
for(n = 1; n < N; ++n)
for(i
= 0; i < num; ++i) //当前n行的状态
for(j = 0; j < num; ++j) //n-1行状态
for(k = 0; k < num; ++k)
//n-2行状态
dp
[i][j] = max(dp
[i][j], dp[n-1][j][k]+状态i的炮兵数);
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 105
#define MAXSIZE 13
int n, m;
int g[MAX];
int
dp[MAX][1<<(MAXSIZE/2)][1<<(MAXSIZE/2)];
int line[MAX], sum[MAX], num; //line保存可行解, sum保存可行解的炮兵数目
inline bool cmp(int x)
{
if( x &
(x<<1) ) return false;
if( x &
(x<<2) ) return false;
return true;
}
inline int max(int a, int b)
{
return a > b ? a : b;
}
inline int getSum(int x)
{
int sum = 0;
while(x > 0)
{
if(x & 1)
sum++;
x
>>= 1;
}
return sum;
}
void init()
{
num = 0;
for(int i = 0; i <
(1<<m); ++i)
{
if( cmp(i) )
{
line[num] =
i;
sum[num++] =
getSum(i);
}
}
}
int DP()
{
int i,j, k, t;
for(i = 0; i < num; ++i)
{
if( !(line[i] &
g[0]) )
dp[0][i][0] =
sum[i];
}
for(i = 1; i < n; ++i)
{
for(j = 0; j <
num; ++j) //i行
{
if(g[i]
& line[j]) continue; //g是H=1,P=0.
g和line相同时为假(匹配地形)
for(k = 0; k
< num; ++k) // i-1行
{
if(line[j]
& line[k]) continue;
for(t
= 0; t < num; ++t) //i-2行
{
if(line[k]
& line[t]) continue;
if(line[j]
& line[t]) continue;
if(dp[i-1][k][t]
== -1) continue;
dp[i][j][k]
= max(dp[i][j][k], dp[i-1][k][t]+sum[j]);
}
}
}
}
int result = 0;
for(i = 0; i < num; ++i)
{
for(j = 0; j <
num; ++j)
result =
max(result, dp[n-1][i][j]);
}
return result;
}
int main()
{
int i, j;
char ch;
// freopen("input.txt", "r", stdin);
while(scanf("%d %d",&n,
&m) != EOF)
{
memset(dp, -1,
sizeof(dp));
memset(g, 0, sizeof(g));
for(i = 0; i <
n; ++i)
{
getchar();
for(j = 0; j
< m; ++j)
{
scanf("%c",&ch);
if(ch
== 'H') g[i] |= (1<<j);
}
}
init();
printf("%d\n", DP());
}
return 0;
}
Description
司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H"
表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
Input
第一行包含两个由空格分割开的正整数,分别表示N和M;
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N
<= 100;M <= 10。
Output
仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。
Sample Input
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
Sample Output
6
题意: 给定一块地形, 包括山丘'H'和平原'P'两种, 平地可以放置炮兵, 每个炮兵的攻击范围
是一个左右各2个格和上下各2个格. 要求炮兵相互不能攻击, 计算最大放置炮兵数.
(山地不可以放置, 平原可以).
解题思路:
1.
题意很清晰, 设状态: dp[i][j][k]表示: 当前第i层状态为j, 第i-1层状态为k,
从0层~i层的在当前状态的炮兵个数.(依然采用状态压缩方法)
2. 动态方程: dp[i][j][k] = (dp[i-1][k][t]+当前层(第i层)的炮兵数) (t是第i-2层状态)
一开始想用递归方法处理DP(d,
i, j, k, count, x, y)//d列,i当前层状态, j上一层状态
k是上一层的上一层状态, count计数炮兵, (x,y)坐标用来判断山地和平原.
DP(d, i, j, k, count, x, y)
{
if(d == m) //最后一列
dp[i][j][k] = (dp[i-1][k][t]+count);
return;
if(d+1 <= m) ....
if(d+3 <= m) ....
}
发现处理边界时候很复杂. 并且每行的状态2^10, 空间复杂度O(100*2^10*1^10)非常大.
这样处理不实际, 这里纠结了好久.
3.
接着想怎么可以将状态减少, 发现因为炮兵的横向攻击范围是左右各2个格, 所有一半的
状态时无效的,
空间复杂度降低到O(100*2^6*2*6). 既然每行可行的状态不多, 可以保存
行的可行解作为预处理(关键).
for(i = 0; i < (1<<m);
++i)
if( !( (i &
(i<<1)) || (i
& (i<<2))
) )
i就是一个可行解.
4. 有了预处理, 用递推方法就方便多了;
for(n = 1; n < N; ++n)
for(i
= 0; i < num; ++i) //当前n行的状态
for(j = 0; j < num; ++j) //n-1行状态
for(k = 0; k < num; ++k)
//n-2行状态
dp
[i][j] = max(dp
[i][j], dp[n-1][j][k]+状态i的炮兵数);
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 105
#define MAXSIZE 13
int n, m;
int g[MAX];
int
dp[MAX][1<<(MAXSIZE/2)][1<<(MAXSIZE/2)];
int line[MAX], sum[MAX], num; //line保存可行解, sum保存可行解的炮兵数目
inline bool cmp(int x)
{
if( x &
(x<<1) ) return false;
if( x &
(x<<2) ) return false;
return true;
}
inline int max(int a, int b)
{
return a > b ? a : b;
}
inline int getSum(int x)
{
int sum = 0;
while(x > 0)
{
if(x & 1)
sum++;
x
>>= 1;
}
return sum;
}
void init()
{
num = 0;
for(int i = 0; i <
(1<<m); ++i)
{
if( cmp(i) )
{
line[num] =
i;
sum[num++] =
getSum(i);
}
}
}
int DP()
{
int i,j, k, t;
for(i = 0; i < num; ++i)
{
if( !(line[i] &
g[0]) )
dp[0][i][0] =
sum[i];
}
for(i = 1; i < n; ++i)
{
for(j = 0; j <
num; ++j) //i行
{
if(g[i]
& line[j]) continue; //g是H=1,P=0.
g和line相同时为假(匹配地形)
for(k = 0; k
< num; ++k) // i-1行
{
if(line[j]
& line[k]) continue;
for(t
= 0; t < num; ++t) //i-2行
{
if(line[k]
& line[t]) continue;
if(line[j]
& line[t]) continue;
if(dp[i-1][k][t]
== -1) continue;
dp[i][j][k]
= max(dp[i][j][k], dp[i-1][k][t]+sum[j]);
}
}
}
}
int result = 0;
for(i = 0; i < num; ++i)
{
for(j = 0; j <
num; ++j)
result =
max(result, dp[n-1][i][j]);
}
return result;
}
int main()
{
int i, j;
char ch;
// freopen("input.txt", "r", stdin);
while(scanf("%d %d",&n,
&m) != EOF)
{
memset(dp, -1,
sizeof(dp));
memset(g, 0, sizeof(g));
for(i = 0; i <
n; ++i)
{
getchar();
for(j = 0; j
< m; ++j)
{
scanf("%c",&ch);
if(ch
== 'H') g[i] |= (1<<j);
}
}
init();
printf("%d\n", DP());
}
return 0;
}
相关文章推荐
- 兴趣学习: 摆脱“三天打渔两天晒网”…
- ACM: 动态规划题 poj 2411
- ACM: 动态规划题 poj 3254 学习状…
- ES使用脚本进行局部更新的排错记录 推荐
- ACM: 动态规划题 poj 3034
- (OK)(OK) Fedora23——NS3(MANETs)——Docker(busybox)——genymotion(android)——All is OK
- windows.h
- ACM: 动态规划题 poj 1925
- ACM: 动态规划题 poj 2948
- ACM: 树状数组 poj 2029 动态规划…
- ACM: 动态规划题 poj3280
- ACM: 动态规划题 poj 1054
- ACM: 并查集 poj 2492
- 兴趣学习: 信息熵 ---- 《数学…
- ACM: 并查集 poj 1703
- ACM: 线段树 poj 3368
- ACM: 线段树 poj 3264
- ACM: 树状数组 poj 3321 图论+树状…
- ACM: 树状数组 poj 2155 学习《浅…
- ACM: 树状数组 poj 1195