Jury Compromise
In Frobnia, a far-awaycountry, the verdicts in court trials are determined by a jury consisting ofmembers of the general public. Every time a trial is set to begin, a jury hasto be selected, which is done as follows. First, several people are drawnrandomly from the public. For each person in this pool, defence and prosecutionassign a grade from 0 to 20 indicating their preference for this person. 0means total dislike, 20 on the other hand means that this person is consideredideally suited for the jury.
Based on the grades of the two parties,the judge selects the jury. In order to ensure a fair trial, the tendencies ofthe jury to favour either defence or prosecution should be as balanced aspossible. The jury therefore has to be chosen in a way that is satisfactory toboth parties.
We will now make this more precise: givena pool of n potential jurors and two values di (the defence's value) and pi(the prosecution's value) for each potential juror i, you are to select a juryof m persons. If J is a subset of {1, ..., n} with m elements, then D(J) = sumdk (k in J) and P(J) = sum pk (k in J) are the total values of this jury fordefence and prosecution.
For an optimal jury J, the value | D(J) -P(J) | must be minimal. If there are several jurys with minimal | D(J) - P(J)|, one which maximizes D(J) + P(J) should be selected since the jury should beas ideal as possible for both parties.
You are to write a program that implementsthis jury selection process and chooses an optimal jury given a set ofcandidates.
Note: If your solution is based on aninefficient algorithm, it may not execute in the allotted time.
Input
The input contains several jury selection rounds. Each round starts with a linecontaining two integers n and m. n is the number of candidates and m the numberof jury members. These values will satisfy 1 <= n <= 200, 1 <= m <=20 and of course m <= n. The following n lines contain the two integers piand di for i = 1, ..., n. A blank line separates each round from the next.
The input ends with a round that has n = m= 0.
Output
For each round output a line containing the number of the jury selection round(`Jury #1', `Jury #2', etc.).
On the next line print the values D(J) andP(J) of your jury as shown below and on another line print the numbers of the mchosen candidates in ascending order. Output a blank before each individualcandidate number.
Output an empty line after each test case.
Sample Input
4 2
1 2
2 3
4 1
6 2
0 0
Sample Output
Jury #1
Best jury has value 6 for prosecution and value 4 for defence:
2 3
题目意思:体重给出n(1<=n<=200)个候选人,每个人都有两种属性d和p(他们都位于1到20 之间),从中选出m(1<=m<=20,且m<=n)个人。要求这m个人满足他们全部d的和减去全部p的和的绝对值最小,如果最小值不止一个,那么久选取全部d的和加上全部p的和最大的那一个。
解题思路:这题很显然要用到动态规划。为了便于描述,我们经d和p的属性放在一个结构体Node中。对于选m个人,∑p-∑d的取值范围是[-20*m,20*m]。用key[i][j]表示从n个人中选出i个,他们的∑p-∑d=j且∑p+∑d最大。如果key[i][j]=-1表示不存在这样的组合,key[i][j]=0是动态规划的起点。则动态规划的转移方程为:
如果key[i-1][s]是一个有效的组合,且结点t不在key[i-1][s]中,
则key[i][s+node[t].p-node[t].d]也是一个有效的组合,如果key[i][s+node[t].p-node[t].d]<key[i-1][s]+node[t].p+node[t].d ,那么就更新。由于在数组中的下标不可能取负数,所以将下标进行一个映射,使其全部非负(下标全部加400即可)。为了判断结点t是否与在key[i-1][s]中出现过,我们需要一个数组path来记录结点出现的顺序。path的维数与key相同。path[i][j]表示满足key[i][j]出现的第一个元素。下一个元素为path[i-1][j-(node[path[i][j]].p- node[path[i][j]].d)。path[i][j]=0则说明不存在路径。
所以具体的代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 300 //候选人人数的最大值
#define maxm 30 //陪审团人数的最大值
struct Node
{
intd;
intp;
};
Node node[maxn];//存放着候选人的分数
int key[maxm][40*maxm];//key[i][j]表示从候选人中选取i个,其∑(d-p)=j且∑(d+p)最大的值
int path[maxm][40*maxm];//path[i][j]存放着当key[i][j]为可行值时所存放着路径
int result[maxm];//存放最终结果
int n,m;
void dp();
int main()
{
intcasenum=1;
cin>>n>>m;
while((n!=0)&&(m!=0))
{
for(inti=1;i<=n;i++)
{
cin>>node[i].p>>node[i].d;
}
cout<<"Jury#"<<casenum<<endl;
casenum+=1;
dp();
cin>>n>>m;
}
return0;
}
void dp()
{
intmaxX=m*20;//选取m个人中差值最大的值
for(inti=0;i<=m;i++)
{
for(intj=0;j<=2*maxX;j++)
{
key[i][j]=-1;//-1表示没有可行方案
path[i][j]=0;//0表示没有路径或者路径到此结束
}
}
key[0][maxX]=0;//dp起点.
for(inti=1;i<=m;i++)
{
for(intj=0;j<=2*maxX;j++)
{
if(key[i-1][j]>=0)//可行方案才进行组合
{
for(intk=1;k<=n;k++)
{
if(key[i-1][j]+node[k].d+node[k].p>key[i][j+node[k].p-node[k].d])
{
/*注意:j+node[k].p-node[k].d]之所以不会越界是因为当j的有效范围是[-20*(i-1),20*(i-1)],就算在两端加上(±)20也不会越界*/
intt1,t2;
t1=i-1;
t2=j;
while((t1>=0)&&(path[t1][t2]!=k))
{
//t2=path[t1][t2];
t2-=node[path[t1][t2]].p-node[path[t1][t2]].d;
t1-=1;
}
if(t1==-1)
{
key[i][j+node[k].p-node[k].d]=key[i-1][j]+node[k].d+node[k].p;
path[i][j+node[k].p-node[k].d]=k;
}
}
}
}
}
}
intmid=maxX;
intincrease=0;
intlast;
while((key[m][mid+increase]<0)&&(key[m][mid-increase]<0))
{
increase+=1;
}
if(key[m][mid+increase]>key[m][mid-increase])
last=mid+increase;
else
last=mid-increase;
cout<<"Bestjury has value "<<(key[m][last]+last-mid)/2<<" forprosecution and value "<<(key[m][last]-last+mid)/2<<" fordefence:"<<endl;
intst=last;
for(inti=m;i>0;i--)
{
result[m-i]=path[i][st];
st-=node[path[i][st]].p-node[path[i][st]].d;
}
sort(result,result+m);
for(inti=0;i<m;i++)
cout<<""<<result[i];
cout<<endl<<endl;
}