poj-1185炮兵阵地

炮兵阵地
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 15742   Accepted: 5957

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

地址:http://poj.org/problem?id=1185

一下午加一晚上,状态压缩dp,实在是。。。。。。。。照着一个很详细的代码和注释,一边敲一边标注释,现在养成了标注释的好习惯,以前以为没有用。。。。解释不出来了。全在注释里,很详细,我的第一道状压dp,例题,也不是我自己做的,我都多久没自己做一道题了,只有在比赛的时候才能真正自己做上一两道。。。。。。。

代码:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int maxx=100000;//列数最大不超过10
int stack[maxx];
int len=0;
char map[105][15];
int n,m,power=1;
int dp[100][100][150];
int find(int now,int last,int n){//now:当前行last:上一行
    int ret=0,sz=0;
    int tmp=stack[now]|stack[last];//放了炮的列数
    if(dp[now][last][n]!=-1)return dp[now][last][n];
    for(int i=0;i<m;++i){
        if((stack[now]&(~(1<<i)))!=stack[now])++sz;//sz为now这行有几个炮
    }
    if(n==1){//到第一行返回第一行有几个炮
        dp[now][last][1]=sz;
        return sz;
    }
    for(int i=0;i<len;++i)//判断now-2行
    {
        int flag=0;
        if(tmp&stack[i])continue;//tmp是底下两行的炮在哪列,与now-2行不能冲突,冲突则continue
        for(int j=0;j<m;++j)
        {
            if(map[n-2][j]=='H'&&(stack[i]&(~(1<<j)))!=stack[i]){
                flag=1;break;
            }
        }
        if(!flag)ret=max(ret,find(last,i,n-1));
    }
    ret+=sz;
    dp[now][last][n]=ret;
    return ret;
}
int main()
{
    int ans=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        map[0][i]='H';
    }
    for(int i=0;i<m;++i){//一行有m个位置,每个位置可以有两种状态,一行共2^m种状态 eg:10010
        power*=2;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%s",map[i]);
    }
    for(int i=0;i<power;++i)//每个i都代表此行的一种状态,从00001到11111共power种
    {
        int flag=0;
        for(int j=0;j<m;++j)//表示第i种状况的第j位,如01001的第2位是1
        {
            if((i&(~(1<<j)))!=i){//如果i状况下第j位是1,即放了炮,那么j-2,j-1,j+1,j+2四个位置均不能放
                if(j-2<=m&&j-2>0&&(i&(~(1<<(j-2))))!=i){//j-2位置在1~m范围内且j-2是1
                    flag=1;
                    break;
                }
                if(j-1<=m&&j-1>0&&(i&(~(1<<(j-1))))!=i){//j-1位置在1~m范围内且j-1是1
                    flag=1;
                    break;
                }
                if(j+1<=m&&j+1>0&&(i&(~(1<<(j+1))))!=i){//j+1位置在1~m范围内且j+1是1
                    flag=1;
                    break;
                }
                if(j+2<=m&&j+2>0&&(i&(~(1<<(j+2))))!=i){//j+2位置在1~m范围内且j+2是1
                    flag=1;
                    break;
                }
            }
        }
        if(!flag)stack[len++]=i;//stack数组里存放的i是一行里所有可能的情况,减去了那些在攻击范围内的不可能情况
    }
    memset(dp,-1,sizeof(dp));
    for(int i=0;i<len;++i)//当前行的情况,有len种
    {
        for(int j=0;j<len;++j){//当前行的上一行,每行的上一行都要考虑len种情况
            int flag=0;
            if((stack[i]&stack[j]))continue;//当前行是stack[i],它的上一行是stack[j],如果两个1同列,那么不合格继续continue
            for(int k=0;k<m;++k){//判断此行中哪个是hill,H不能放炮
                if(map[n][k]=='H'&&(stack[i]&(~(1<<k)))!=stack[i]){//k位置有炮而且k位置是hill
                    flag=1;break;
                }
                if(map[n-1][k]=='H'&&(stack[j]&(~(1<<k)))!=stack[j]){//上一行k位置有炮而且k位置是hill
                    flag=1;break;
                }
            }
            if(!flag)ans=max(ans,find(i,j,n));
        }
    }
    printf("%d\n",ans);

    return 0;
}
View Code

 

posted @ 2013-08-09 21:02  SunnySnail  阅读(230)  评论(0编辑  收藏  举报