思路和网上的都差不多, 我另外开了程序算出每一行可能的情况直接copy进 stack[] 中..耍流氓.. 看起来直观点
引用一下大牛BYVoid的思路, 不自己写了
较难看出的动态规划问题。注意到数据范围N≤100;M≤10,发现每行的宽度都不大,所以可以考虑把一行看成一个状态,表示该行的布置方案。每行的布置方案可以预先处理出,由数学知识可以算出,每行最多的布置方案数也不过60个而已。
状态设定
F[i,a,b]为前i行,第i行的方案为A[a],第i-1行的方案为A[b]的最大炮兵数。
状态转移方程
- F[i,a,b]=Max{ F[i-1,b,c] + P[a] }
其中c为i-2行的布置方案,a,b,c应保证互不冲突,即放置方案中炮兵都在平地上,且不会互相攻击到。
目标结果
- Ans=Max{F[N,a,b]}
中间进行过一次错误的优化, 认为如果, 比如
画个图..
- HHPH
PPPP
PPPP
HHPH (红色代表有炮)
1001比1101少了一个1, 那么1101得出的结果至少不会比1001差, 可以剪掉1001. 比如上图的第三行, 没有炮的情况就可以省去,.我当时的想法是如果省掉第三行的炮, 放在第四行唯一的P上(其他情况也是相似), 第四行以下面临的情况就会比之前更加严峻, 从而不会得出更好的结果, 顶多一样.
但是我并没有考虑第三行能对第一行也产生影响 (造成这个错误的原因是状态压缩DP只记录近两行. 虽然没有明面上写, 但是情况已经记录在得分中了). 第三行的P不放炮, 则第一行和第四行都能放, 结果就更好了.
遗留问题, 如何才能在类的定义中实现二维数组的动态分配?
我写成这样
public: int *(a[60]); State (int m=0) { M=m; for(int i=0;i<stackN;i++) { a[i]=new int[stackN]; memset(a[i],0xff,sizeof(int)*stackN); } }
能够compile, 在另开的程序里也没有问题, 但是在题目中答案就不一样, 我跟踪了, 在 s[i].dp(s[i-1]) 的过程中 s[i-1].a[][] 会莫名发生改变, 实际上又没有对它进行任何操作. 值得一提的是release和debug得出的答案不一样, 根据经验应该是内存的什么问题.. 也有可能是类里动态分配内存就不对头, 因为在类中定义变量不能用new.. 开学去问问看
Code: (其实是我第一次用类.. 这个问题有点绕, 于是尝试封装=D )
#include <stdio.h> #include <string.h> int stack[]={ 0,1,2,4,8,9,16,17,18,32,33,34,36,64,65,66,68, 72,73,128,129,130,132,136,137,144,145,146,256, 257,258,260,264,265,272,273,274,288,289,290,292, 512,513,514,516,520,521,528,529,530,544,545,546, 548,576,577,578,580,584,585,2E9 }; //All legal possibilities of assembles, denary. int stackT[]={ 0,1,1,1,1,2,1,2,2,1,2,2,2,1,2,2,2,2,3,1, 2,2,2,2,3,2,3,3,1,2,2,2,2,3,2,3,3,2,3,3, 3,1,2,2,2,2,3,2,3,3,2,3,3,3,2,3,3,3,3,4 }; //How many 1 are there in stack. int stackN; class State { private: int M; bool map[10]; bool ok(int foo); public: int a[60][60]; State (int m=0) { M=m; memset(a,0xff,sizeof(a)); } void dp(State &last); void readMap(); }; bool State::ok(int foo) { for(int i=0;i<M;i++) { if( ((foo>>i)&1) && map[i]) return false; } return true; } void State::readMap() { char t; for(int j=0;j<M;j++) { while(1) { t=getchar(); if(t=='H' || t=='P') break; } map[j]= t=='H'?1:0; } return; } void State::dp(State &last) { int temp; for(int i=stackN-1;i>=0;i--) { for(int j=0;j<stackN;j++) { //if(a[i][j]==-2) //continue; for(int k=0;k<stackN;k++) { if( (stack[j]|stack[k]) & stack[i] ) //Whether two states engage. continue; if(!ok(stack[i])) continue; if(last.a[j][k]<0 ) //Doesn't exist. continue; temp=last.a[j][k]+stackT[i]; if(a[i][j]<temp) a[i][j]=temp; /* for(int h=0;h<i;h++) if( (stack[h]|stack[i])==stack[i] ) a[h][j]=-2; */ //WRONG! } } } return; } int main() { int M,N; scanf("%d%d",&N,&M); if(N<0 || M<0) { printf("0\n"); return 0; } for(stackN=0;stack[stackN]<(1<<M);stackN++); //Set edge of stack according to the width M. State templt(M); State *s=new State[N+1]; for(int i=1;i<=N;i++) s[i]=templt; //State s[101]=State(M); s[0].a[0][0]=0; //Initialize. while(getchar()!='\n'); for(int i=1;i<=N;i++) s[i].readMap(); for(int i=1;i<=N;i++) s[i].dp(s[i-1]); int max=0; for(int i=0;i<stackN;i++) for(int j=0;j<stackN;j++) if(max<s[N].a[i][j]) max=s[N].a[i][j]; printf("%d\n",max); delete []s; return 0; }