POJ1015陪审团(Jury Compromise)——dp+路径记录
题目:http://poj.org/problem?id=1015
差值是有后效性的,所以“转化为可行性”,开一维记录“能否达到这个差值”。
当然可以开两维分别记录 a 和 b,但 “值只是0或1” 和 “当前元素对应一个 a 只有一个 b ,其他 b 就浪费了” 这两点说明这种状态有优化空间。
开一维记录差值,d数组的值记录最大的 a+b 值较好。
虽然差值是绝对值,但需要分正负以计算。可以全部加一个修正值 fix 解决脚标非负的问题。(不要试图开一维0或1记录正负,会在边界的地方分类讨论而死)
然后是重点!!!如何记录路径???
用了LICS记录路径的自己的那个方法:记一个pre,记一个use。但本题中的意义与那题略有不同。自我感觉还不错。虽然慢到了469ms。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int fix=405; int n,m,a[205],b[205],d[25][815],pre[205][25][815],ls,xnt,use[205][25][815]; void print(int i,int j,int k,int as,int bs) { if(!j) { printf("Best jury has value %d for prosecution and value %d for defence:\n",as,bs); return; } print(pre[i][j][k],j-1,k-(a[i]-b[i]),as+a[i],bs+b[i]);//pre只存了阶段,j和k还得自己传参 printf("%d ",i); } int main() { while(++xnt) { scanf("%d%d",&n,&m); if(!n&&!m)return 0; const int lm=20*m; for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]); memset(d,-2,sizeof d); memset(pre,0,sizeof pre); d[0][fix]=0; for(int i=1;i<=n;i++) { memcpy(use[i],use[i-1],sizeof use[i-1]);//如果什么都不做,就是沿用着i-1的值(省去的那一维) memcpy(pre[i],pre[i-1],sizeof pre[i-1]);//pre记录上一步是哪个阶段 for(int j=m;j;j--) //(对于没有用当前的i的情况,没什么用) { int k=a[i]-b[i]; for(int l=-lm+fix;l<=lm+fix;l++) if(l-k>=-lm+fix&&l-k<=lm+fix&&d[j-1][l-k]+a[i]+b[i]>d[j][l]) { d[j][l]=d[j-1][l-k]+a[i]+b[i]; use[i][j][l]=i;//在i这个阶段,达到j和l的是第i号元素 if(use[i-1][j-1][l-k]==i-1) //第几个阶段就是第几号元素。因第i阶段正在考虑第i号元素 pre[i][j][l]=i-1; else pre[i][j][l]=use[i-1][j-1][l-k];//暨第几阶段 } } //use和pre的分离只是为了那个判断,能在被用的元素处pre停留一下 } //但他们的意义还是有不同的 for(int i=fix,j=fix;i<=lm+fix&&j>=-lm+fix;i++,j--) if(d[m][i]>=0||d[m][j]>=0) { if(d[m][i]>=0)ls=i; if(d[m][j]>d[m][i])ls=j; break; } printf("Jury #%d\n",xnt); if(use[n][m][ls]==n) { print(pre[n][m][ls],m-1,ls-(a[n]-b[n]),a[n],b[n]); printf("%d ",n); } else print(use[n][m][ls],m,ls,0,0);//不是pre!! printf("\n"); } }
然而有100ms左右的方法,还有各种各样的:http://www.cnblogs.com/Zinn/p/8571061.html
记录路径是一门学问。