HDU4539

来源:互联网 发布:python 技术指标 编辑:程序博客网 时间:2024/04/30 08:52

HDU4539 郑厂长系列故事——排兵布阵

郑厂长不是正厂长

也不是副厂长

他根本就不是厂长

事实上

他是带兵打仗的团长

  一天,郑厂长带着他的军队来到了一个n*m的平原准备布阵。

  根据以往的战斗经验,每个士兵可以攻击到并且只能攻击到与之曼哈顿距离为2的位置以及士兵本身所在的位置。当然,一个士兵不能站在另外一个士兵所能攻击到的位置,同时因为地形的原因平原上也不是每一个位置都可以安排士兵。

现在,已知n,m 以及平原阵地的具体地形,请你帮助郑厂长计算该阵地,最多能安排多少个士兵。

输入包含多组测试数据;

输入:每组数据的第一行包含2个整数n和m (n <= 100, m <= 10 ),之间用空格隔开;接下来的n行,每行m个数,表示n*m的矩形阵地,其中1表示该位置可以安排士兵,0表示该地形不允许安排士兵。

输出:请为每组数据计算并输出最多能安排的士兵数量,每组数据输出一行。

分析:

首先注意本题求的是最多能安排多少士兵,而不是最多有多少种安排方式。

首先可知从第0行到第n-1行依次安排的话,当前如果在安排第i行的士兵,那么当前的安排方式只与第i-1排和第i-2排的安排方式有关。所以我们想当以两行士兵的安排结果作为状态进行转移。用d[i][s1][s2]表示第i行士兵的安排方式为s1(以s1的二进制形式表示)时且第i-1行士兵的安排方式为s2时,最多能安排多少士兵。

假设d[1][3][0]=2,由于3的二进制形式为0011,0的二进制形式就是0000,则d[1][3][0]=2的含义为当第1行的安排方式为0011时,第0行的安排方式为0000,且第0行之前的所有行不互相冲突时,能安排的士兵最大数目为2.

状态转移方程为:d[i][s1][s2]=max{d[i-1][s2][s3]+bitcount(s1)} 其中bitcount(s1)表示s1的二进制形式中的1的个数,要求s1,s2,s3组成的二进制形式兼容(即要求s1,s2,s3的任意两个士兵之间的曼哈顿距离不为2),由于s1,s2,s3要求兼容,我们可以先用get计算出所有兼容的3元组(s1,s2,s3)然后一一取值带入DP状态转移方程即可。求兼容模式的时候注意,题目中还有一些矩阵格子是不能放士兵的。

初始值为:d[0][x][0]=bitcount(x),其中x取0到(1<<m)-1的闭区间。

代码:此代码超时且内存溢出,由于要计算100*1000*1000种状态,且每次计算还要判断状态之间是否兼容,状态是否合法。


#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int d[100+5][(1<<10)+10][(1<<10)+10];int s1[1<<10],s2[1<<10],s3[1<<10];int a[110][10];int n,m,cnt;bool compa(int s1,int s2,int r)//判断S1是否与S2的二进制序列兼容且他们相差r行(r为1或2),S1为第i行S2为第i-r行{    bool ok = true;    if(r==1)    {        for(int i=0; i<m; i++)if(s1&(1<<i)) //s1第i位为1            {                if(i>0&& (s2&(1<<(i-1))) )//s2第i-1位为1                {                    ok = false;                    break;                }                if(i<m-1&& (s2&(1<<(i+1))) )//s2第i+1位为1                {                    ok = false;                    break;                }            }    }    else//s1与s2对应位至少有一个不为1    {        if(s1&s2)ok=false;    }    return ok;}bool legal(int r ,int s)//s的二进制形式是否符合a[i]的二进制形式且s中间隔1个空位的1{    for(int i=0; i<m; i++)if(!a[r][m-1-i] && (s&(1<<i)) )            return false;    for(int i=0;i<m-2;i++)if((s&(1<<i)) && (s&(1<<(i+2))))            return false;    return true;}int bitcount(int x){    if(x==0)return 0;    return (x&1)+bitcount(x/2);}int main(){    while(scanf("%d%d",&n,&m)==2&&n&&m)    {        for(int i=0; i<n; i++)            for(int j=0; j<m; j++)                scanf("%d",&a[i][j]);        int sum = 0;        memset(d,0,sizeof(d));        for(int i=0; i<(1<<m); i++)if(legal(0,i))        {            d[0][i][0]=bitcount(i);            if(n==1) sum = max(sum,d[0][i][0]);        }        for(int i=0; i<(1<<m); i++)if(legal(1,i)) //第1行的d[1][][]单独计算        {                for(int j=0; j<(1<<m); j++)if(legal(0,j)) //第0行的                    {                        if( compa(i,j,1) )                            d[1][i][j]=bitcount(i)+bitcount(j);                            if(n==2) sum = max(sum,d[1][i][j]);                    }        }        for(int r=2; r<n; r++)        {            for(int i=0; i<(1<<m); i++)if(legal(r,i))            {                    for(int j=0; j<(1<<m); j++)if(legal(r-1,j))                    {                            for(int k=0; k<(1<<m); k++)if(legal(r-2,k))                            {                                //printf("当前处理第%d行 i j k %d %d %d\n",r,i,j,k);                                //print(i);print(j);print(k);                                    if( compa(i,j,1) && compa(j,k,1) && compa(i,k,2) )//3对二进制兼容                                    {                                        //printf("兼容");                                        d[r][i][j]=max( d[r][i][j],d[r-1][j][k]+bitcount(i) );                                        if(r==n-1) sum = max(sum,d[r][i][j]);                                    }                                    else                                    {                                        ;//printf("不兼容");                                    }                                    //printf("\n");                            }                    }            }        }        printf("%d\n",sum);    }    return 0;}


其实我们想想就可以发现对于一个10位01序列,并不是所有的都合法,我们不用每次从0到1023中去选,我们只需要先找出这1024个01序列中,那些序列本身不会有两个1间一隔位置的,排除这些,剩下的才是我们每次都需要去判断是否兼容和合法的序列。

以下程序还做了位运算等方面的优化.

AC代码:390MS

//#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn = 202;int d[100+5][maxn][maxn];int a[110][10];//用于存原始矩阵int b[110];//用于存原始矩阵中每行生成的二进制数int n,m,cnt;int v[maxn],num[maxn];inline bool compa(int s1,int s2,int r)//判断S1是否与S2的二进制序列兼容且他们相差r行(r为1或2),S1为第i行S2为第i-r行{    s1 = v[s1];    s2 = v[s2];    bool ok = true;    if(r==1)    {        if( (s1&(s2<<1)) || (s2&(s1<<1) ) )            ok = false;    }    else//s1与s2对应位至少有一个不为1    {        if(s1&s2)ok=false;    }    return ok;}inline bool legal(int r ,int s)//s的二进制形式是否符合b[i]的二进制形式{    s=v[s];    if(b[r]&s)            return false;    return true;}int bitcount(int x)//计算x的二进制形式中有多少个1{    if(x==0)return 0;    return (x&1)+bitcount(x/2);}int main(){    while(scanf("%d%d",&n,&m)==2&&n&&m)    {        for(int i=0; i<n; i++)        {            b[i]=0;//b[i]用于保存读入的二进制序列的 "反码"            for(int j=0; j<m; j++)            {                scanf("%d",&a[i][j]);                if(!a[i][j])b[i] = b[i]|(1<<(j));//注意这里是对于原矩阵不为1的位置取1            }        }        int sum = 0;//计算能放士兵的最大数目        cnt= 0;        for(int i=0;i<(1<<m);i++)//计算不含间1隔的那些固定二进制序列        {            if(i&(i<<2))                continue;            num[cnt]=bitcount(i);//保留这个序列的1位数            v[cnt++]=i;//记录这个序列        }        memset(d,0,sizeof(d));        for(int i=0; i<cnt; i++)if(legal(0,i))//计算第0行的d        {            d[0][i][0]=num[i];            if(n==1) sum = max(sum,d[0][i][0]);        }        for(int i=0; i<cnt; i++)if(legal(1,i)) //第1行的d[1][][]单独计算,可以不单独计算,但是要弄清楚数据关系        {                for(int j=0; j<cnt; j++)if(legal(0,j)) //第0行可能出现的合法二进制序列                    {                        if( compa(i,j,1) )//兼容                            d[1][i][j]=num[i]+num[j];                            if(n==2) sum = max(sum,d[1][i][j]);                    }        }        for(int r=2; r<n; r++)//从第二行开始计算        {            for(int i=0; i<(cnt); i++)if(legal(r,i))//第r行取V[i]序列            {                    for(int j=0; j<(cnt); j++)if(legal(r-1,j))//第r-1行取v[j]序列                    {                            for(int k=0; k<(cnt); k++)if(legal(r-2,k))//第r-1行取v[k]序列                            {                                    if( compa(i,j,1) && compa(j,k,1) && compa(i,k,2) )//3对二进制序列兼容                                    {                                        d[r][i][j]=max( d[r][i][j],d[r-1][j][k]+num[i] );                                        if(r==n-1) sum = max(sum,d[r][i][j]);//是否最大值                                    }                            }                    }            }        }        printf("%d\n",sum);    }    return 0;}


 

0 0
原创粉丝点击