思路:
用f[i][j][k]表示从前i个人中选j个人且总差为k的所有方案中的总分最大的方案的总分。(好好理一理)
我们维护这个数组,再找出最小的差值v,那么最大分数的方案即为f[n][m][v]所对应的方案。
/!由于分数0——20,那么一个人的差值(p-d)范围在-20——20,20个人总差值就为-400——400。
定一个偏移量base=400,全部加上base即可使总差值变为0——800。!/
代码段落:
输入——维护f数组——找最小差值v——找入选的人——计算人选的两个总分——输出
分段讲解:
A:维护f数组。
对于第i个人,只有选与不选的两种情况。
一、不选,则f[i][j][k]=f[i-1][j][k]。
二、选,那么i需要减少,j(还未入选人数)也要减少,且k(总差值)应减去第i人的差值(p[i]-d[i])。
由于f表示总分,所以还需要加上第i人的总分(d[i]+p[i])。
综上,f[i][j][k]=f[i-1][j-1][k-(p[i]-d[i])]+d[i]+p[i];
注意,应该先判断是否能加入第i人,也就是说要先判断j减去1后会不会小于0,
以及k减去第i人的差值后会不会越界(也就是小于0,或大于最大值800)。
最后从选与不选中选择可行且值最大的方案。
B:找最小差值v。
我们先将v设为0(也就是最理想的情况)。
然后判断,方法是看f[n][m][v+base]和f[n][m][base-v]是否都小于0,
若都小于0,则说明不存在此种情况,则v++,利用while循环找出最小的v值。
此外可能出现base+v和base-v都合法的情况,则需要判断哪一个的总分最大(就是将f值比大小)
将v赋成那个方案所对应的v值(注意加上base)
C:找入选的人。
方法是反过来推。首先从f[i][j][v](i=n,j=m,这里的v是上面找出来的最小差值加上base)开始,
判断他是由哪一种情况得来(选与不选),若是不选,直接将i--;若选,记录人选,将v减掉该人的差值,
再i--,j--。
附上完整代码:
#include<bits/stdc++.h>
const int N=210,M=810,base=400;//base是偏移量,将差值全部变成非负数。
int n,m,d[N],p[N];
int f[N][21][M];
int ren[N],num=0;
using namespace std;
int main(){
int T=1;//测试组数
while(scanf("%d%d",&n,&m),n||m){
for(int i=1;i<=n;i++) scanf("%d%d",&p[i],&d[i]);
memset(f,-0x3f,sizeof f);//由于选最大值,初始化时全部赋为负无穷。
f[0][0][base]=0;//合法状态。
//维护f数组(见上方A)
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
for(int k=0;k<M;k++){
f[i][j][k]=f[i-1][j][k];//不选i时情况。
int c=k-p[i]+d[i];
if(j-1<0) continue;//保证可以选i。
if(c<0||c>800) continue;//保证可以选i。
else f[i][j][k]=max(f[i][j][k],f[i-1][j-1][c]+p[i]+d[i]);//最终值取选i与不选i中总分最大的情况。
}
}
}
//找最小差值v(见上方B)
int v=0;
while(f[n][m][base+v]<0&&f[n][m][base-v]<0) v++;//找v
if(f[n][m][base-v]>f[n][m][base+v]) v=base-v;//选择最佳方案。
else v=base+v;
//找入选的人(见上方C)。
int i=n,j=m;
int zp=0,zd=0;//zp为P总分,zd为D总分。
while(j){
if(f[i][j][v]==f[i-1][j][v]) i--;//没入选。
else {
ren[++num]=i;
v-=(p[i]-d[i]);
i--;
j--;//入选。
}
}
//通过得出的人选计算两个总分。
for(int i=1;i<=num;i++){
zd+=d[ren[i]];
zp+=p[ren[i]];
}
//输出。
printf("Jury #%d\n",T++);
printf("Best jury has value %d for prosecution and value %d for defence:\n",zp,zd);
for(int i=1;i<=num;++i){
printf(" %d",ren[i]);
}
puts("\n");
num=0;//!!!!记得最后归零,不然下一组测试将包含上一组的答案。
}
return 0;
}
我觉得
f[i-1][j-1][k - (d[i] - p[i])] + d[i] + p[i]
会更好理解一点f[i][j][k]=f[i-1][j-1][k-(p[i]-d[i])]+d[i]+p[i];
为啥是p[i] - d[i]
不应该是d[i] - p[i]
吗?你愿意也可以做
感谢佬的代码
你这个输出方案没有升序。
哈哈哈,哪里都有群友
等一下,你的答案错了
nan
····万古神犇SBM,扑通扑通跪下来