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;}
- HDU4539
- HDU4539+状态压缩DP
- hdu4539(状态压缩)
- hdu4539(状态压缩)
- hdu4539状态压缩
- hdu4539(状压dp)
- hdu4539之状态压缩dp
- hdu4539 状态压缩dp水题
- hdu4539 排兵布阵 状态DP
- HDU4539:郑厂长系列故事——排兵布阵(状态压缩)
- hdu4539 郑厂长系列故事——排兵布阵 + POJ1158 炮兵阵地
- hdu4539郑厂长系列故事——排兵布阵 dp+状态压缩
- hdu4539 郑厂长系列故事——排兵布阵(状压dp)
- HDU4539 郑厂长系列故事——排兵布阵【状压DP】
- (待解决) hdu HDU4539 郑厂长系列故事——排兵布阵 (状态压缩dp~)
- whatsApp 被收购随想: 做工具累积用户,跟巨头合作实现平台收益
- K近邻分类算法实现 in Python
- 在虚拟机(centos)配置postgresql数据库(1) - 安装篇
- 简化文件的绝对路径 Simpify Path
- C++ traits和enable_if的实现
- HDU4539
- 用ant实现SVN代码更新,部署
- apache虚拟主机
- OGNL表达式和EL表达式
- ortp库API说明
- MyEclipse去掉debug模式
- Unity学习(二):一些游戏开发中的术语(未完待续)
- [博弈]HDU 2509 Be the Winner
- C++调用gSoap编写的WEBSERVICE与C#.NET间接口自定义结构体不能重复使用