读完题,很容易想到这是一道背包问题 ,但就是做不来,可以看到,方案既与差值有关,又跟和有关,那么可以考虑根据差值或和来考虑状态,可以考虑f[i][j][k]f[i][j][k]表示前ii个人选了jj个人并且差值为kk的和最大的方案

那么状态该如何转移呢?

考虑第ii个人选或不选

若不选第ii个人

很明显f[i][j][k]=f[i1][j][k])f[i][j][k]=f[i-1][j][k])

若选第ii个人

ii个人的差值已经定了,那么前ii个人的差值就是k(p[i]d[i])k-(p[i]-d[i])

可以得出f[i][j][k]=max(f[i][j][k],f[i1][j1[k(p[i]d[i])])f[i][j][k]=max(f[i][j][k],f[i-1][j-1[k-(p[i]-d[i])])

从题目中可以看出,差值的范围[400,400]\left [-400,400 \right]那么可以直接枚举所有差值转移即可

解决了第一小问,接下来就是输出方案,可以考虑把每个状态从哪一个状态转移存起来,可以通过判断f[i][j][k]f[i][j][k]f[i1][j][k]f[i-1][j][k]的关系,若相等,则说明可以不选ii,若不等,则说明ii一定被选

代码如下

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

int f[205][25][805], p[205], d[205], tot;

void output(int i,int j,int k)//输出方案
{
int ans[25], cnt = 0, P = 0, D = 0;
while(j)
{
if(f[i][j][k] == f[i - 1][j][k])
i--;
else
{
ans[++cnt] = i;
k -= (p[i] - d[i]);
P += p[i];
D += d[i];
j--, i--;
}
}
printf("Jury #%d\n", ++tot);
printf("Best jury has value %d for prosecution and value %d for defence:\n", P, D);
for (int u = cnt; u >= 1;u--)
printf(" %d", ans[u]);
printf("\n\n");//注意要多输出一个换行
}

int main()
{
int n, m;
while(scanf("%d%d",&n,&m)&&(n!=0||m!=0))//读入多组数据
{
for (int i = 1; i <= n;i++)
scanf("%d%d", &p[i], &d[i]);
memset(f, -0x3f, sizeof(f));
f[0][0][400] = 0;
for (int i = 1; i <= n;i++)
{
for (int j = 0; j <= m;j++)
{
for (int k = 0; k <= 800; k++)
{
f[i][j][k] = f[i - 1][j][k];
if(j < 1)//因为可以一个人都不选,所以j从0开始,所以要判断j是否等于0
continue;
int t = k - p[i] + d[i];
if(t < 0 || t > 800)//判断是否越界
continue;
f[i][j][k] = max(f[i - 1][j - 1][t] + p[i] + d[i], f[i][j][k]);
}
}
}
for (int i = 0; i <= 400; i++)//找出差值最小的方案
{
if(f[n][m][400-i] >= 0 ||f[n][m][i+400] >= 0)
{
if(f[n][m][400-i] > f[n][m][400+i])
{
output(n, m, 400 - i);
break;
}
else
{
output(n, m, 400 + i);
break;
}
}
}
}
return 0;
}