题解 UVA323 【Jury Compromise】
读完题,很容易想到这是一道背包问题 ,但就是做不来,可以看到,方案既与差值有关,又跟和有关,那么可以考虑根据差值或和来考虑状态,可以考虑\(f[i][j][k]\)表示前\(i\)个人选了\(j\)个人并且差值为\(k\)的和最大的方案
那么状态该如何转移呢?
考虑第\(i\)个人选或不选
若不选第\(i\)个人
很明显\(f[i][j][k]=f[i-1][j][k])\)
若选第\(i\)个人
第\(i\)个人的差值已经定了,那么前\(i\)个人的差值就是\(k-(p[i]-d[i])\)
可以得出\(f[i][j][k]=max(f[i][j][k],f[i-1][j-1[k-(p[i]-d[i])])\)
从题目中可以看出,差值的范围\(\left [-400,400 \right]\)那么可以直接枚举所有差值转移即可
解决了第一小问,接下来就是输出方案,可以考虑把每个状态从哪一个状态转移存起来,可以通过判断\(f[i][j][k]\)与\(f[i-1][j][k]\)的关系,若相等,则说明可以不选\(i\),若不等,则说明\(i\)一定被选
代码如下
#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;
}

浙公网安备 33010602011771号