Poj 1185/Hrbust 1844 炮兵阵地【状压dp】

来源:互联网 发布:轻而易举瓷砖软件 编辑:程序博客网 时间:2024/06/16 21:08


炮兵阵地
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 25502 Accepted: 9836

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


思路:

如果小伙伴们对状压dp还不是很了解的话,大家可以先考虑考虑做做这个题并参考参考我的详解:http://blog.csdn.net/mengxiang000000/article/details/51075506


1、设定dp【i】【j】【k】表示现在dp过程到了第i行,第i行状态为j第i-1行状态为k的最大放置炮台的个数。对于状态来讲,我们使用十进制数来保存,其含义为:其转化成2进制之后,对应位数字为1表示这个位子上放置炮台,数字为0表示这个位子上不放置炮台。


2、那么不难理解:

dp【i】【j】【k】=max(dp【i】【j】【k】,dp【i-1】【k】【l】+第i行状态j放置的炮台个数);

虽然这个状态转移方程式一点问题都没有,但是呢,它的设定会超内存。所以呢,我们再次设定:

dp【i】【j】【k】表示现在dp过程到了第i行,第i行的可行状态编号为j,第k行的可行状态编号为k的最大放置炮台的个数

e.g:某一行:PHHP

可行方案有三种:

8

9

对应我们给其编号为;

1  1

2 8

3 9

因为可行方案并不会太多,所以我们可以更多的释放内存。


3、那么我们在输入完图之后,初始化搞定这个可行状态的保存问题:

①设定tmp=这一行H的情况,e.g:PHPP,tmp=2;(我们设定最左边的是第一位)

②枚举这一行所有可能的状态j【(0<=j<(1<<m)】,然后判断这种状态是否合法,首先,j&tmp==0,表示在H的地方,我们没有放置不合法的炮台。

③然后我们将这种状态保存入数组a【i】【contz】,并且记录当前状态中有多少个1(就是有多少个炮台),记录在add【i】【contz】中。

对应代码:

int judge(int j,int k,int l){    if((j&k)!=0)return 0;    if((j&l)!=0)return 0;    if((k&l)!=0)return 0;    return 1;} int end=(1<<m);        for(int i=0;i<n;i++)        {            int tmp=0;            int contz=0;            for(int j=0;j<m;j++)            {                if(aa[i][j]=='H')                {                    tmp+=(1<<j);                }            }            for(int j=0;j<end;j++)            {                if(judgerow(j)==1)                {                    if((j&tmp)==0)                    {                        int sum=0;                        int tmpp=j;                        while(tmpp)                        {                            if((tmpp&1)!=0)sum++;                            tmpp/=2;                        }                        a[i][contz]=j;                        add[i][contz++]=sum;                    }                }            }            kk[i]=contz;        }




4、然后我们进行状态转移,我们从第二行开始搞定这个问题(如果输入只有一行,我们单独处理)

①枚举第i行的状态j和i-1行的状态k,得到dp【i】【j】【k】的三个元素i,j,k。

②然后枚举一个状态l,得到dp【i-1】【k】【l】的三个元素i-1,k,l。

③然后判断一下j,k,l三个状态有没有上下矛盾的情况,如果没有,维护dp【i】【j】【k】的值。

对应代码:


int judgerow(int j){    if((j&(j<<1))!=0)return 0;    if((j&(j<<2))!=0)return 0;    if((j&(j>>1))!=0)return 0;    if((j&(j>>2))!=0)return 0;    return 1;}for(int i=1;i<n;i++)        {            for(int jj=0;jj<kk[i];jj++)            {                for(int ll=0;ll<kk[i-1];ll++)                {                    int j=jj;                    int k=ll;                    int addd=add[i][jj];                    if(i==1)                    {                        addd+=add[i-1][ll];                        if(judge(a[i][j],a[i-1][k],0)==1)                        {                            dp[i][j][k]=max(dp[i][j][k],addd);                        }                        continue;                    }                    for(int pp=0;pp<kk[i-2];pp++)                    {                        int l=pp;                        if(judge(a[i][j],a[i-1][k],a[i-2][l])==1)                        {                            dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+addd);                        }                    }                }            }        }


5、整个思路和核心代码搞定了之后:

ans=max(dp【n-1】【i】【j】);

维护一下即可。


完整Ac代码:


#include<stdio.h>#include<iostream>#include<string.h>using namespace std;//int dp[150][1024][1024];int dp[150][120][120];int kk[150];int add[150][1050];int a[150][1050];char aa[150][20];int n,m;int judge(int j,int k,int l){    if((j&k)!=0)return 0;    if((j&l)!=0)return 0;    if((k&l)!=0)return 0;    return 1;}int judgerow(int j){    if((j&(j<<1))!=0)return 0;    if((j&(j<<2))!=0)return 0;    if((j&(j>>1))!=0)return 0;    if((j&(j>>2))!=0)return 0;    return 1;}int main(){    while(~scanf("%d%d",&n,&m))    {        memset(dp,0,sizeof(dp));        for(int i=0;i<n;i++)        {            scanf("%s",aa[i]);        }        int end=(1<<m);        for(int i=0;i<n;i++)        {            int tmp=0;            int contz=0;            for(int j=0;j<m;j++)            {                if(aa[i][j]=='H')                {                    tmp+=(1<<j);                }            }            for(int j=0;j<end;j++)            {                if(judgerow(j)==1)                {                    if((j&tmp)==0)                    {                        int sum=0;                        int tmpp=j;                        while(tmpp)                        {                            if((tmpp&1)!=0)sum++;                            tmpp/=2;                        }                        a[i][contz]=j;                        add[i][contz++]=sum;                    }                }            }            kk[i]=contz;        }        for(int i=1;i<n;i++)        {            for(int jj=0;jj<kk[i];jj++)            {                for(int ll=0;ll<kk[i-1];ll++)                {                    int j=jj;                    int k=ll;                    int addd=add[i][jj];                    if(i==1)                    {                        addd+=add[i-1][ll];                        if(judge(a[i][j],a[i-1][k],0)==1)                        {                            dp[i][j][k]=max(dp[i][j][k],addd);                        }                        continue;                    }                    for(int pp=0;pp<kk[i-2];pp++)                    {                        int l=pp;                        if(judge(a[i][j],a[i-1][k],a[i-2][l])==1)                        {                            dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+addd);                        }                    }                }            }        }        if(n==1)        {            int ans=0;            for(int i=0;i<kk[0];i++)            {                ans=max(add[0][i],ans);            }            printf("%d\n",ans);            continue;        }        int ans=0;        for(int i=0;i<kk[n-1];i++)        {            for(int j=0;j<kk[n-2];j++)            {                ans=max(dp[n-1][i][j],ans);            }        }        printf("%d\n",ans);    }}






0 0