Time Limit: 1000MS | Memory Limit: 65536K | |||
Total Submissions: 29953 | Accepted: 7982 | Special Judge |
Description
Based on the grades of the two parties, the judge selects the jury. In order to ensure a fair trial, the tendencies of the jury to favour either defence or prosecution should be as balanced as possible. The jury therefore has to be chosen in a way that is satisfactory to both parties.
We will now make this more precise: given a 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 jury of m persons. If J is a subset of {1,..., n} with m elements, then D(J ) = sum(dk) k belong to J
and P(J) = sum(pk) k belong to J are the total values of this jury for defence 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 be as ideal as possible for both parties.
You are to write a program that implements this jury selection process and chooses an optimal jury given a set of candidates.
Input
These values will satisfy 1<=n<=200, 1<=m<=20 and of course m<=n. The following n lines contain the two integers pi and di for i = 1,...,n. A blank line separates each round from the next.
The file ends with a round that has n = m = 0.
Output
On the next line print the values D(J ) and P (J ) of your jury as shown below and on another line print the numbers of the m chosen candidates in ascending order. Output a blank before each individual candidate 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
Hint
Source
大致题意:
在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n 个人作为陪审团的候选人,然后再从这n 个人中选m 人组成陪审团。选m 人的办法是:控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0 到20。为了公平起见,法官选出陪审团的原则是:选出的m 个人,必须满足辩方总分D和控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的 |D-P| 值相同,那么选辩控双方总分之和D+P最大的方案即可。
输出:
选取符合条件的最优m个候选人后,要求输出这m个人的辩方总值D和控方总值P,并升序输出他们的编号。
可行方案dp(j-1, x)能演化成方案dp(j, k)的必要条件是:存在某个候选人i,i 在方案dp(j-1, x)中没有被选上,且x+V(i) = k。在所有满足该必要条件的dp(j-1, x)中,选出 dp(j-1, x) + S(i) 的值最大的那个,那么方案dp(j-1, x)再加上候选人i,就演变成了方案 dp(j, k)。
这中间需要将一个方案都选了哪些人都记录下来。不妨将方案dp(j, k)中最后选的那个候选人的编号,记在二维数组的元素path[j][k]中。那么方案dp(j, k)的倒数第二个人选的编号,就是path[j-1][k-V[path[j][k]]]。假定最后算出了解方案的辩控差是k,那么从path[m][k]出发,就能顺藤摸瓜一步步回溯求出所有被选中的候选人。
//china no.1
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <vector>
#include <iostream>
#include <string>
#include <map>
#include <stack>
#include <cstring>
#include <queue>
#include <list>
#include <stdio.h>
#include <set>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <cctype>
#include <sstream>
#include <functional>
#include <stdlib.h>
#include <time.h>
#include <bitset>
using namespace std;
#define pi acos(-1)
#define PI acos(-1)
#define endl '\n'
#define srand() srand(time(0));
#define me(x,y) memset(x,y,sizeof(x));
#define foreach(it,a) for(__typeof((a).begin()) it=(a).begin();it!=(a).end();it++)
#define close() ios::sync_with_stdio(0); cin.tie(0);
#define FOR(x,n,i) for(int i=x;i<=n;i++)
#define FOr(x,n,i) for(int i=x;i<n;i++)
#define W while
#define sgn(x) ((x) < 0 ? -1 : (x) > 0)
#define bug printf("***********\n");
#define db double
#define ll long long
typedef long long LL;
const int INF=0x3f3f3f3f;
const LL LINF=0x3f3f3f3f3f3f3f3fLL;
const int dx[]={-1,0,1,0,1,-1,-1,1};
const int dy[]={0,1,0,-1,-1,1,-1,1};
const int maxn=1e3+10;
const int maxx=1e6+100;
const double EPS=1e-8;
const double eps=1e-8;
const int mod=1e9+7;
template<class T>inline T min(T a,T b,T c) { return min(min(a,b),c);}
template<class T>inline T max(T a,T b,T c) { return max(max(a,b),c);}
template<class T>inline T min(T a,T b,T c,T d) { return min(min(a,b),min(c,d));}
template<class T>inline T max(T a,T b,T c,T d) { return max(max(a,b),max(c,d));}
template <class T>
inline bool scan_d(T &ret){char c;int sgn;if (c = getchar(), c == EOF){return 0;}
while (c != '-' && (c < '0' || c > '9')){c = getchar();}sgn = (c == '-') ? -1 : 1;ret = (c == '-') ? 0 : (c - '0');
while (c = getchar(), c >= '0' && c <= '9'){ret = ret * 10 + (c - '0');}ret *= sgn;return 1;}
inline bool scan_lf(double &num){char in;double Dec=0.1;bool IsN=false,IsD=false;in=getchar();if(in==EOF) return false;
while(in!='-'&&in!='.'&&(in<'0'||in>'9'))in=getchar();if(in=='-'){IsN=true;num=0;}else if(in=='.'){IsD=true;num=0;}
else num=in-'0';if(!IsD){while(in=getchar(),in>='0'&&in<='9'){num*=10;num+=in-'0';}}
if(in!='.'){if(IsN) num=-num;return true;}else{while(in=getchar(),in>='0'&&in<='9'){num+=Dec*(in-'0');Dec*=0.1;}}
if(IsN) num=-num;return true;}
void Out(LL a){if(a < 0) { putchar('-'); a = -a; }if(a >= 10) Out(a / 10);putchar(a % 10 + '0');}
void print(LL a){ Out(a),puts("");}
//freopen( "in.txt" , "r" , stdin );
//freopen( "data.txt" , "w" , stdout );
//cerr << "run time is " << clock() << endl;
int n,m;
int dp[25][maxn],path[25][maxn];
//dp[j][k]:取j个候选人,使其辩控差为k的所有方案中,辩控和最大的方案的辩控和
//path 路径
int s[maxn],v[maxn],id[25];
/*回溯,确认dp[j][k]方案是否曾选择过候选人i*/
bool select(int j,int k,int i)
{
while(j>0 && path[j][k]!=i)
{
k-=v[path[j][k]];
j--;
}
return j?false:true;
}
int main()
{
int cas=1;
W(scanf("%d%d",&n,&m),n+m)
{
me(dp,-1);
me(path,0);
FOR(1,n,i)
{
int p,d;
scan_d(d);
scan_d(p);
s[i]=d+p;
v[i]=d-p;
}
int fix=m*20;//总修正值,修正极限为从[-400,400]映射到[0,800]
dp[0][fix]=0;//dp[0][0]
FOR(1,m,j)
FOR(0,2*fix,k)
{
if(dp[j-1][k]>=0)
{
FOR(1,n,i)
{
if(dp[j][k+v[i]]<dp[j-1][k]+s[i])
{
if(select(j-1,k,i))
{
dp[j][k+v[i]]=dp[j-1][k]+s[i];
path[j][k+v[i]]=i;
}
}
}
}
}
int k;
for(k=0;k<=fix;k++)
if(dp[m][fix-k]>=0||dp[m][fix+k]>=0)
break;
int div=dp[m][fix-k]>dp[m][fix+k]?(fix-k):(fix+k); //最小辨控差
for(int p=div,i=1;i<=m;i++)
{
id[i]=path[m-i+1][p];
p-=v[id[i]];
}
sort(id+1,id+m+1);
printf("Jury #%d\n",cas++);
printf("Best jury has value %d for prosecution and value %d for defence:\n",(dp[m][div]+div-fix)/2,(dp[m][div]-div+fix)/2);
FOR(1,m,i) printf(" %d",id[i]);printf("\n\n");
}
}
上述代码考虑不完全。path记录路径是有问题的。
下面正解:
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int dp[21][801];
vector<int> path[21][801];
int main()
{
int times=1;
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
int subtraction[201],_plus[201];
int n,m,i,j,k;
while(~scanf("%d%d",&n,&m) && n && m)
{
for(i=0;i<m;++i)//清空vector
for(j=0;j<801;++j)
path[i][j].clear();
memset(dp,-1,sizeof(dp));
int d,p;
for(i = 0; i < n; i++)
{
cin>>d>>p;
subtraction[i] = d-p;
_plus[i] = d+p;
}
int fix = 20*m;
dp[0][fix] = 0;
for(k = 0; k < n; k++)//选择一个
for(i = m-1; i >= 0; i--)//进行逆推
{
for(j = 0; j < 2*fix; j++)
{
if(dp[i][j] >= 0)
{
if(dp[i+1][j+subtraction[k]] <= dp[i][j] + _plus[k])
{
dp[i+1][j+subtraction[k]] = dp[i][j] + _plus[k];
path[i+1][j+subtraction[k]] = path[i][j];//每次更新都要把path全部复制过来,就是因为这个才用的vector
path[i+1][j+subtraction[k]].push_back(k);
}
}
}
}
for(i = 0; dp[m][fix+i] == -1 && dp[m][fix-i] == -1; i++);
int temp = (dp[m][fix+i] > dp[m][fix-i]) ? i : -i;
int sumD = ( dp[m][fix+temp] + temp )/2;
int sumP = ( dp[m][fix+temp] - temp )/2;
printf( "Jury #%d\n", times++ );
printf( "Best jury has value %d for prosecution and value %d for defence:\n", sumD,sumP);
for( i=0; i < m; i++ )
printf( " %d", path[m][fix+temp][i]+1);
printf( "\n\n" );
}
return 0;
}