题目大意:
在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n个人作为陪审团的候选人,然后再从这n个人中选m人组成陪审团。选m人的办法是: 控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0到20。为了公平起见,法官选出陪审团的原则是:选出的m个人,必须满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案的辩方总分和控方总分的之差的绝对值相同,那么选辩控双方总分之和最大的方案即可。
Input
输入包含多组数据。每组数据的第一行是两个整数n和m,n是候选人数目,m是陪审团人数。注意,1<=n<=200, 1<=m<=20 而且 m<=n。接下来的n行,每行表示一个候选人的信息,它包含2个整数,先后是控方和辩方对该候选人的打分。候选人按出现的先后从1开始编号。两组有效数据之间以空行分隔。最后一组数据n=m=0
Output
对每组数据,先输出一行,表示答案所属的组号,如 'Jury #1', 'Jury #2', 等。接下来的一行要象例子那样输出陪审团的控方总分和辩方总分。再下来一行要以升序输出陪审团里每个成员的编号,两个成员编号之间用空格分隔。每组输出数据须以一个空行结束。
Sample Input
4 2 1 2 2 3 4 1 6 2 0 0Sample Output
Jury #1 Best jury has value 6 for prosecution and value 4 for defence: 2 3Hint
If your solution is based on an inefficient algorithm, it may not execute in the allowed time.
题解:
一道很不错的dp,需要进行一些巧妙的转换。
首先我们把每个人看成容量,那么最大容量V就变成了m,相当于把n个物品放入容量为m的背包里面,而且每个物品的容量为1,之后我们不难想出p-v就是每个物品的费用。最后考虑滚动数组优化,我们逆序枚举容量(即选i个人),可以费用的范围是[-400,400],比较小,那么我们就可以考虑枚举所有的费用情况,同时维护一下保证d+p最大就行了。
状态:dp[i][j]表示选i个人(即容量),辩控差(即费用)为j的所有方案中最大辩控和的方案。
状态转移方程:dp[i+1][j+sub[k]]=max(dp[i+1][j+sub[k]],dp[i][[j]+add[k])
其实就是选i+1个人,辩控差为j+sub[k]的最优方案是由所有能够通过选i个人,辩控差为j再加上一个人k的贡献达到这个状态的辩控和最大的那个方案。
因为这个费用可能为负,所以我们要考虑把[-400,400]映射到[0,800],相当于加了一个修正值fix=20*m.
中间计量记录答案的时候注意记录一下路径就行了,至于求被选择的最优解的d+p总和以及d-p总和,我的方法是枚举路径中的点,记录答案就行了,有一个简单的公式可以记录答案,不过不是特别理解,就有简单的方法直接求的。
代码实现:
#pragma GCC optimize(2) #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<cstdlib> #include<vector> #include<map> #include<set> #include<stack> #include<queue> #define PI atan(1.0)*4 #define E 2.718281828 #define rp(i,s,t) for (register int i = (s); i <= (t); i++) #define RP(i,t,s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a,b) memset(a,b,sizeof(a)) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define pii pair<int,int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a=0,b=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') b=-1; c=getchar(); } while(c>='0'&&c<='9') { a=(a<<3)+(a<<1)+c-'0'; c=getchar(); } return a*b; } const int INF = 0x3f3f3f3f; const int N = 1e3+7; int n,m; int sub[205],add[205],d[205],p[205]; int dp[25][805]; vector<int> path[25][805]; void init(){ rp(i,0,m-1) rp(j,0,801) path[i][j].clear(); mst(dp,-1); } int main(){ int kcase=0; while(~scanf("%d%d",&n,&m)&&n&&m){ rp(i,0,n-1){ d[i]=read(),p[i]=read(); sub[i]=d[i]-p[i]; add[i]=d[i]+p[i]; } init(); int fix=m*20; dp[0][fix]=0; rp(k,0,n-1){ RP(i,m-1,0){ rp(j,0,2*fix-1){ if(dp[i][j]>=0){ if(dp[i+1][j+sub[k]]<=dp[i][j]+add[k]){ dp[i+1][j+sub[k]]=dp[i][j]+add[k]; path[i+1][j+sub[k]]=path[i][j]; path[i+1][j+sub[k]].pb(k); } } } } } int i; 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;//记录最优解的状态,同时保证d+p的和最大 printf("Jury #%d\n",++kcase); int sumD=0,sumP=0; rp(i,0,m-1){ sumD+=d[path[m][fix+temp][i]]; sumP+=p[path[m][fix+temp][i]]; } printf("Best jury has value %d for prosecution and value %d for defence:\n",sumD,sumP); rp(i,0,m-1) printf(" %d",path[m][fix+temp][i]+1); printf("\n\n"); } return 0; } /* 1 7 3 3 3 6 6 2 2 */
dp训练计划——poj1015(01背包变形+路径输出)
猜你喜欢
转载自blog.csdn.net/qq_43472263/article/details/104615640
今日推荐
周排行