1 条题解

  • 1
    @ 2022-10-8 19:45:27

    每日一题 #041 2022.10.7

    POJ P1015 Jury Compromise

    Statement

    选一个陪审团,先随机挑选 nn 个人作为陪审团的候选人,然后选 mm 人组成陪审团。控方和辩方会给候选人打分。请做到选出的 mm 个人满足辩方总分和控方总分的差的绝对值最小。如果有多种选择方案,那么选辩控双方总分之和最大的方案。如果方案还是不惟一,输出任意一种均可。

    • n200n\le 200
    • m20,mnm\le 20,m\le n
    • 任何分值不超过 2020

    Tutorial

    Click to see

    虽然数据范围不大,以为是搜索,但其实是受了“宇宙射线”影响的超级变形背包。可能连一点背包的影子都不见了。

    考虑 DP。设状态 f(i,j,k)f(i,j,k) 表示前 ii 个人中选 jj 个人,得分差为 k400k-400,最大的得分总和。注意这里得分差不是绝对值差,为了防止下标出现负数,差需要加上 base=400base=400

    这道题难于找出答案的具体方案。在 DP 过程中,把所有不合法解全部标为负无穷,从 basebase 处剖开,不难发现,最终状态中找到的第一个合法状态,就是我们想要的。后面的倒退不难,注意输出格式。

    为什么这样就解决问题了呢?要求差最小,把 basebase 看作 00,也就是对于 v,vv,-v,如果状态 f(n,m,v+base)f(n,m,v+base) 合法,vv 的绝对值一定是最小的。至于和最大,DP 迭代过程中已经计算出来了。

    // 陪审团
    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <cstdio>
    #include <algorithm>
    #define SIZE 205
    #define all(x) x.begin(), x.end()
    #define debug(x) cout<<#x<<":"<<x<<endl;
    using namespace std;
    
    int n, m; 
    int p[SIZE], d[SIZE];
    int base=400;
    int f[SIZE][21][1000];
    
    signed main()
    {
    	int COUNT=1;
    	while(cin>>n>>m && n+m)
    	{
    		for(int i=1; i<=n; i++) cin>>p[i]>>d[i];
    		memset(f, -0x3f, sizeof(f));
    		f[0][0][base]=0;
    		for(int i=1; i<=n; i++)
    			for(int j=0; j<=m; j++) 
    				for(int k=0; k<1000; k++)
    				{
    					int tmp=k-(p[i]-d[i]);
    					if(tmp<0 || tmp>=1000)
    					{
    						f[i][j][k]=f[i-1][j][k]; continue;
    					}
    					if(!j)
    					{
    						f[i][j][k]=f[i-1][j][k]; continue;
    					}
    					f[i][j][k]=max(f[i-1][j][k], f[i-1][j-1][tmp]+p[i]+d[i]);
    				}
    		int v=0;
            while(f[n][m][base-v]<0 && f[n][m][base+v]<0) v++;
            if(f[n][m][base-v]>f[n][m][base+v]) v=base-v;
            else v=base+v;
        	
            vector<int> ans;
            int i=n, j=m, k=v;
            while(j)
            {
            	if(f[i][j][k]==f[i-1][j][k]) i--;
            	else
            	{
            		ans.push_back(i); k-=(p[i]-d[i]);
            		i--, j--;
    			}
    		}
    		
    		int sp=0, sd=0;
    		for(int i=0; i<ans.size(); i++)
    			sp+=p[ans[i]], sd+=d[ans[i]];
    		printf("Jury #%d\n", COUNT++);
    		printf("Best jury has value %d for prosecution and value %d for defence:\n", sp, sd);
    		sort(all(ans));
    		for(int i=0; i<ans.size(); i++)
    			cout<<" "<<ans[i];
    		cout<<endl; cout<<endl;
    	}
    
        return 0;
    }
    
    • 1

    信息

    ID
    16
    时间
    1000ms
    内存
    64MiB
    难度
    8
    标签
    (无)
    递交数
    18
    已通过
    7
    上传者