炮兵阵地

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

分析:思路很简单,因为每个位置要么放一门炮、要么不放,每行最多只有10个位置,所以可以用位存储状态,大体分如下两步:
1.计算出每行所有的合法状态,并用位表示。
2.DP~~~
f[p][i][j] = Max{f[p-1][j][k]+ct(p,i)}    f[p][i][j]表示第p行取第i种方案,p-1行取第j种方案时的最大值,ct(p,i)表示第p行用第i种方案时用的炮的个数。 求整数x用二进制表示时1的个数,只需要让x不断的执行x &= (x-1)就可以了,执行了多少次就有多少个1。
为什么呢??可以分两种情况,如果x最后一位是1,那么执行一次与运算可以把这个1消掉,但是如果最后一位是0呢??这个时候消掉的是最靠右的一个1,呵呵,可以简单的画一下看看。总之就是逐步消掉最后一个1,消了几次就有几个1啦~~ 第一个代码如下:
 # include<iostream>
# include<cstdio>
# include<cstring>
using namespace std; const int N = ;
const int M = ;
const int INF = 0xffffff; int n,m;
int ct[N][]; //ct[i][0]表示第i行放置炮的方案数,ct[i][j],j>0 表示第j种方案的状态
int tt[]; //tt[i]表示 i 表示成2进制时的1的个数,即放置炮的数目
int f[N][][]; //f[p][i][j]表示第p行取第i种方案,p-1行取第j种方案时的最大值
int hash[][];
char map[N][M]; int max(int x,int y){
return x>y ?x : y;
} //这一行的状态
void Init_row(const int &r,int x,int key){ //r是第几行,x表示2进制的位数,key表示状态
if(x==m){
ct[r][] ++; //ct[r][0]表示这一行状态的数目
ct[r][ct[r][]] = key; //ct[r][i]表示第r行第i个状态 被压缩后的数字
return ;
}
if(map[r][x] == 'P' && (key & )== &&(key & )==) //可以表示成状态
Init_row(r,x+,(key<<)+);
Init_row(r,x+,key<<);
} bool Yes(int x,int y){ //判断map[x][y]能不能放置炮,会不会进入其他人射程
int xx=x,yy=y;
if(hash[x][y] != -) return hash[x][y];
while(x||y){
if((x&)== && (y&)==){
hash[xx][yy] = false;
break;
}
x>>=;
y>>=;
}
if(hash[xx][yy] == -) hash[xx][yy] = ;
hash[yy][xx] = hash[xx][yy];
return hash[xx][yy];
} void Dp(){
int i,j,k,p,u,v,w;
for(i=;i<=ct[][];i++) f[][i][] = tt[ct[][i]]; //第1行放置炮的数目
if(n>=) //第2行放置炮的数目
for(i=;i<=ct[][];i++){
u=ct[][i];
for(j=;j<=ct[][];j++){
f[][i][j] = -INF;
v = ct[][j];
if(Yes(u,v))
f[][i][j] = max(f[][i][j],f[][j][]+tt[u]);
}
} for(p=;p<=n;p++){
for(k=;k<=ct[p][];k++){
w=ct[p][k];
for(i=;i<=ct[p-][];i++){
v=ct[p-][i];
f[p][k][i] = -INF;
if(!Yes(v,w)) continue;
for(j=;j<=ct[p-][];j++){
u=ct[p-][j];
if(Yes(u,v) && Yes(u,w))
f[p][k][i] = max(f[p][k][i],f[p-][i][j] + tt[w]);
}
}
}
}
} int main(){
int i,j,temp,ans;
memset(hash,-,sizeof(hash));
for(i=;i<;i++){
temp = i;
tt[i] = ;
while(temp){ //整数temp表示成2进制时1的个数
tt[i]++;
temp &= (temp -);
}
}
scanf("%d%d",&n,&m);
for(i=;i<=n;i++)
scanf("%s",map[i]);
for(i=;i<=n;i++){
ct[i][] = ;
Init_row(i,,);
}
Dp();
ans = -INF;
if(n==)
for(i=;i<=ct[n][];i++)
ans = max(ans,f[n][i][]);
else{
for(i=;i<=ct[n][];i++)
for(j=;j<=ct[n-][];j++)
ans = max(ans,f[n][i][j]);
}
printf("%d\n",ans);
return ;
}

  由于M很小,最多为10,所以可以对行进行状态压缩。二进制对应位为1表示放炮兵,为0表示空。我们可以事先生成所有有效的状态,即二进制数任何两个1都要相差两位以上,同时用数组记下此状态有多少个炮兵。对于地形也进行状态压缩,用1表求高地,0表示平原。判断某个状态能否放到某个地形,就是地形状态为1的地方,放置炮兵状态一定为0,这点可以用位运算解决。判断两个状态能否放在相邻行与此相同。

代码如下:
 # include <iostream>
using namespace std; const int G = ;
const int N =;
const int M =; int d[][G][G];//滚动数组
int ph[N],f[G];//ph数组用于判断状态在第i行是否满足在P的平原设置,f数组则存放满足的状态
int n,m,g;//g为所有状态数 int OneC(int x){//计算x二进制1的个数,这个算法很优秀,是在《编程之美》书中学到的。
int t =;
while(x){
t ++;
x &= (x-);
}
return t;
}
void DP(){//dp
for(int k =; k < n ;k++){
for(int i =; i< g; i++){
if(ph[k] != (ph[k] | f[i]))continue;//判断状态f[i]是否满足在平原设置炮台
for(int j = ;j < g; j++){
if(ph[k-] != (ph[k-] | f[j]))continue;
if(f[i] & f[j])continue;//判断第k行和第k-1行的炮台是否有彼此击中
for(int q=; q< g; q ++){
if(ph[k-] != (ph[k-] | f[q]))continue;
if(f[q] & f[j])continue;
if(f[i] & f[q])continue;
d[k%][i][j] = max(d[k%][i][j], d[(k+)%][j][q] + OneC(f[i])); //状态方程
}
}
}
}
}
int main(){
scanf("%d %d",&n,&m);
int i ,j;
char ch[M];
for(i =;i < n; i++){//计算ph[]
ph[i] =;
scanf("%s", ch);
for(j =; j < m; j++){
if(ch[j] == 'P')
ph[i] += (<<(m-j-));
}
}
int v = <<m;
for(i =,g =; i< v;i++){//挑选合法状态
if(((i & (i << )) == ) && (i & (i <<))==)//这个想法不错哦。
f[g++] = i;
}
int pMax ;
if(n ==){
pMax = ;
for(i =; i<g ; i++){
if((ph[] | f[i])==ph[])
pMax = max( pMax , OneC(f[i]));
}
printf("%d" ,pMax);
return ;
}
memset(d, ,sizeof(d));
pMax = ;
for(i =; i < g; i++){//初始化d
if(ph[] != (ph[] | f[i]))continue;
for(j = ;j < g; j++){
if(ph[] != (ph[] | f[j]))continue;
if((f[i] & f[j]) ==){
d[][i][j] = OneC(f[i]) + OneC(f[j]);
pMax = max (pMax , d[][i][j]);
}
}
}
if(n ==){
printf("%d" ,pMax);
return ;
}
DP();
for(i =; i < g; i ++){
for(j =; j< g; j++){
pMax = max( pMax ,d[(n+)%][i][j]);
}
}
printf("%d", pMax);
return ;
}

 代码如下:

 # include<cstdio>
# include<string>
# include<cstring>
# include<iostream>
# include<cmath>
# include<algorithm>
using namespace std;
int n,m,sum,num,sta[<<],cot[<<],dp[][][],a[];
bool fit(int x,int y)
{
if(x&y)
return ;
return ;
}
void init()
{
sum=<<m;num=;
for(int i=;i<sum;i++)
{
if(i&(i<<)||i&(i<<))
continue;
sta[num]=i;
int temp = i,count=;
while(temp)
{
count++;
temp&=temp-;
}
cot[num++]=count;
}
}
void DP()
{
int ans=;
for(int i=;i<num;i++)
{
if(!fit(a[],sta[i]))
continue;
dp[][][i] = cot[i];
if(ans<dp[][][i])
ans=dp[][][i];
}
for(int i=;i<=n;i++)
for(int j=;j<num;j++)
for(int k=;k<num;k++)
{
if(!fit(sta[k],sta[j])||!fit(a[i],sta[k])||!fit(a[i-],sta[j]))
continue;
for(int l=;l<num;l++)
{
if(!fit(sta[k],sta[l])||!fit(sta[j],sta[l])||!fit(a[i-],sta[l])||!dp[i-][l][j])
continue;
dp[i][j][k]=max(dp[i][j][k],dp[i-][l][j]+cot[k]);
if(ans<dp[i][j][k])
ans=dp[i][j][k];
}
}
printf("%d\n",ans);
}
int main()
{
char s;
scanf("%d%d",&n,&m);
getchar();
init();
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
scanf("%c",&s);
if(s=='H')
{
int tem=<<(j-);
a[i]+=tem;
}
}
getchar();
}
DP();
return ;
}
另附转的一篇http://www.cnblogs.com/acm-bingzi/p/3278901.html

POJ 1185 炮兵阵地(动态规划+状态压缩)的更多相关文章

  1. POJ 1185 炮兵阵地 (状态压缩DP)

    题目链接 Description 司令部的将军们打算在NM的网格地图上部署他们的炮兵部队.一个NM的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用& ...

  2. poj 1185 炮兵阵地 [经典状态压缩DP]

    题意:略. 思路:由于每个大炮射程为2,所以如果对每一行状态压缩的话,能对它造成影响的就是上面的两行. 这里用dp[row][state1][state2]表示第row行状态为state2,第row- ...

  3. poj 1185 炮兵阵地(三维状态压缩dP)

    题目:http://poj.org/problem?id=1185 思路: d[i][j][k]表示第i行的状态为第k个状态,第i-1行的状态为第j个状态的时候 的炮的数量. 1表示放大炮, 地形状态 ...

  4. POJ 1185 炮兵阵地(状态压缩DP)

    题解:nState为状态数,state数组为可能的状态 代码: #include <map> #include <set> #include <list> #inc ...

  5. POJ 1185 炮兵阵地 状压dp

    题目链接: http://poj.org/problem?id=1185 炮兵阵地 Time Limit: 2000MS Memory Limit: 65536K 问题描述 司令部的将军们打算在N*M ...

  6. POJ 1185 炮兵阵地 经典的 状态压缩dp

    炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 16619   Accepted: 6325 Description ...

  7. POJ 1185 炮兵阵地(经典的状态压缩DP)

    题意:中文题. 思路,经典的状态压缩题目. 由于列长比较小,我们可以以行为阶段用状态压缩来做. 由于攻击只占两个格,这样从行的角度看,第i行的炮兵只与前i-1和前i-2行有关系.这样如果用j,k,l分 ...

  8. POJ 1185 炮兵阵地(状压DP)

    炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 26426   Accepted: 10185 Descriptio ...

  9. POJ 1185 - 炮兵阵地 & HDU 4539 - 郑厂长系列故事——排兵布阵 - [状压DP]

    印象中这道题好像我曾经肝过,但是没肝出来,现在肝出来了也挺开心的 题目链接:http://poj.org/problem?id=1185 Time Limit: 2000MS Memory Limit ...

随机推荐

  1. 将多个Sheet导入到同一个Excel文件中

    实体类excel import java.util.List; /** * 功能: * 描述: * @author * @date 2015-3-19 下午5:15:48 */ public clas ...

  2. [zz] makefile中=和:=的区别

    转载自:http://www.cnblogs.com/wanqieddy/archive/2011/09/21/2184257.html 在Makefile中我们经常看到 = := ?= +=这几个赋 ...

  3. 【转】git - 简明指南

    git - 简明指南 助你入门 git 的简明指南,木有高深内容 ;) 作者:罗杰·杜德勒 感谢:@tfnico, @fhd 和 Namics其他语言 english, deutsch, españo ...

  4. 浏览器兼容console对象的简要解决方案

    不同浏览器或者版本之间对于console对象的支持不尽相同,而console方法在开发调试过程中都是不错的工具.难道要在上线前把所有console.xxxx去掉以保证某些浏览器不报错么.其实可以变通解 ...

  5. iBatis自动生成的主键 (Oracle,MS Sql Server,MySQL)【转】

    iBatis的sqlMap配置文件的selectKey元素有个type属性,可以指定pre或者post表示前生成(pre)还是后生成(post). Oracle设置 <!-- Oracle SE ...

  6. [TypeScript] Using Lodash in TypeScript with Typings and SystemJS

    One of the most confusing parts of getting started with TypeScript is figuring out how to use all th ...

  7. PMP考试的过与只是

    我在一年多时间里參加了三次PMP考试,前两次都失败,直到第三次才成功.怎样对待失败?这是每个人都会遇到的挑战.假设我们能用正确的态度对待临时的失败,那么终于的成功也就不远了.我希望通过本文与大家分享一 ...

  8. linux进程间通讯-System V IPC 信号量

    进程间通信的机制--信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的很多其它内容,能够阅读我的还有一篇文章:Linux进程间通信--使用信号.以下就进入信号量的 ...

  9. JavaEE SSH框架整合(三) struts2 异常、http错误状态码处理

    struts2的action可能出现訪问不到,或action报异常等情况,所以须要作一些处理,给用户一个友好的印象. 1. 异常处理  result声明在action中 <action name ...

  10. ViewPager中使用自定义的ListView实例

    这篇内容是上一篇的延续,因为在上一篇的测试ViewPager成功了,才能实现这一篇的和ListView合在一起使用 效果图如下: 不愿意说理论,直接上代码 1.清单文件 activity_main.x ...