您的位置:首页 > 其它

POJ 1185 炮兵阵地 (状压DP || 最大团算法)

2016-10-12 22:59 411 查看
思路:

直接说思路吧,毕竟中文题目。

先说说正解:

状压dp:令dp[i][j][k],表示当前枚举到第i 行,第i行的状态为j,前一行的状态为k,所得到最大的炮车数量!

内存的优化:

分析一下知道,m = 10,状态就有1 << 10  = 1000种,这样开出来的数组 是 dp[100][1024][1024],太浪费内存!

因此我们可以先预处理状态来优化!这个题目两个炮车之间的空位置必须大于2,因此那些任意两个1之间的空位置小于等于2的,我们都不要了,这样存到一个数组mb[]里,m=10的话 跑一跑程序知道,最多60种!

因此内存我们可以优化到:dp[100][60][60],轻松搞定!

转移思路:

在说说转移的方式:

我们直接枚举每一行 ,先预处理第一行,直接算出每一个状态有几个1即可!

然后枚举下面的,在单独处理第二行!其余的行,就统一处理了,枚举当前行i的状态,和i-1行的状态,i-2行的状态(如果有i-2行的话),然后看看这些状态冲突不冲突,(这些都是位运算了,仔细考虑后 还是比较简单的!)

最后在统计最后一行的最多数量即可了!

=======

在说一说非正解思路:

这个思路只是想到了而已,简单记录下吧:

最大团算法,这个算法也是比较适合解决 放最多棋子的问题!

我们直接给每一个位置进行标号!

然后任意两个号如果不能相互攻击到的话,就给他们连一条线!

然后最后求这个图的最大团即可!

用最大团方法需要注意:

可能有的模板  最大团最小值是1,但这个题目 最小值可以是0!注意一下即可!

不过 勉强过去了!

限时2s的题 ,这个方法 竟然 1954ms 过了 真是给跪了!! 也是可能这个题数据比较水!

=======

详细见代码:(状压dp)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int maxn = 100 + 10;
char s[maxn][17];
int dp[maxn][67][67];
int number[maxn];
int mb[maxn],cnt;
int n,m;
stack<int>bug;
int getnum(int i){
int ret = 0;
while(i){
i &= i-1;
++ret;
}
return ret;
}
//void debug(int i){
// while(i){
// bug.push(i%2);
// i/=2;
// }
// while(!bug.empty()){
// printf("%d",bug.top());
// bug.pop();
// }
// puts("");
//}
void init(){
cnt = 0;
for (int i = 0; i < (1<<m); ++i){
if ( (i & (i << 1)) || (i & (i << 2)) )continue;
mb[cnt++] = i;
}
}
bool check(int i,int j){
int t = i | (~j);
return getnum(~t) == 0;
}
int main(){
// init();
while(scanf("%d %d",&n, &m) == 2){
memset(dp,-1,sizeof dp);
for (int i = 1; i <= n; ++i){
scanf("%s",s[i] + 1);
}
for (int i = 1; i <= n; ++i){
int v = 0;
for (int j = 1; j <= m; ++j){
v = v*2 + (s[i][j] == 'P');
}
number[i] = v;
}
init();
for (int i = 0; i < cnt; ++i){
if (check(number[1],mb[i])){
dp[1][i][0] = max(dp[1][i][0],getnum(mb[i]));
}
}
for (int i = 2; i <= n; ++i){
for (int j = 0; j < cnt; ++j){ // the i'th row;
if (!check(number[i],mb[j]))continue;
for (int k = 0; k < cnt; ++k){ // the i-1'th row;
if (!check(number[i-1],mb[k]))continue;
if (mb[k] & mb[j])continue;
if (i == 2){
dp[i][j][k] = max(getnum(mb[j] + mb[k]),dp[i][j][k]);
}
else {
for (int l = 0; l < cnt; ++l){ // the i-2'th row;
if (!check(number[i-2],mb[l]))continue;
if (mb[k] & mb[l])continue;
if (mb[j] & mb[l])continue;
if (dp[i-1][k][l] == -1)continue;
dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][l] + getnum(mb[j]));
}
}
}
}
}
int ans = 0;
if (n == 1){
for (int i = 0; i < cnt; ++i){
ans = max(ans,dp[1][i][0]);
}
}
else {
for (int i = 0; i < cnt; ++i){ // the n'th row;
for (int j = 0; j < cnt; ++j){ // the n-1'th row;
if (!check(number
,mb[i]) || !check(number[n-1],mb[j]))continue;
if (mb[i] & mb[j])continue;
ans = max(ans,dp
[i ][j ]);
}
}
}
printf("%d\n",ans);

}

return 0;
}


最大团思路代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1000 + 100;
char s[107][17];
int g[1007][1007];
int mc[1007];
int len[1007];
int ans;
bool found;
int n;
int List[maxn][maxn];
void dfs(int Size){
int i, j, k;
if (len[Size] == 0){
if (Size > ans){
ans = Size;
found = true;
}
return ;
}
for (k = 0; k<len[Size] && !found; ++k){
if (Size + len[Size]-k <= ans)
break;
i = List[Size][k];
if (Size+mc[i] <= ans)
break;
for (j = k+1,len[Size+1] = 0; j < len[Size]; ++j)
if (g[i][List[Size][j] ])
List[Size+1][len[Size+1]++ ] = List[Size][j];
dfs(Size+1);
}
}
void max_cluster(){
int i,j;
mc
= ans = 1;
for (i= n-1; i; --i){
found = false;
len[1] = 0;
for (j = i+1; j <= n; ++j)
if (g[i][j])
List[1][len[1]++ ] = j;
dfs(1);
mc[i] = ans;
}
}
int r,c;
void init(){
memset(g,0,sizeof g);
memset(mc,0,sizeof mc);
memset(len,0,sizeof len);
memset(List,0,sizeof List);
for (int i = 1; i <= r; ++i){
for (int j = 1; j <= c; ++j){
if (s[i][j] == 'H')continue;
for (int k = 1; k <= r; ++k){
for (int l = 1; l <= c; ++l){
if (s[k][l] == 'H' || (k == i && l == j))continue;
bool ok = 1;
if (k == i){
if (abs(j-l) <= 2)ok=0;
}
else if (j == l){
if (abs(i-k) <= 2)ok=0;
}
if (ok){
g[(i-1)*c + j][(k-1)*c+l ] = 1;
// printf("%d - %d\n",(i-1)*c + j,(k-1)*c+l);
}
}
}

}

}

}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
while(scanf("%d %d",&r,&c) == 2){
n = (r-1)*c+c;
for (int i = 1; i <= r; ++i){
scanf("%s",s[i]+1);
}
int sum = 0;
for (int i =1; i <= r; ++i){
for (int j = 1; j <= c; ++j){
sum += (s[i][j] == 'P');

}

}
if (!sum){
printf("0\n");
continue;
}
init();
max_cluster();
printf("%d\n",ans);
}
return 0;
}

炮兵阵地

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 26348 Accepted: 10157
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

Source

Noi 01
[Submit]   [Go Back]   [Status]  
[Discuss]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: