陪审团

题目链接:

https://www.acwing.com/problem/content/description/282/

题意:

有n个人,每个人有p,d两种属性,现在要从中选出m个人,使得p属性之和与d属性之和差的绝对值最小,若有多个方案有相同的最小绝对值差,取p+d最大的方案,并输出方案。(1≤N≤200,1≤M≤20,0≤p[i],d[i]≤20)

思路:

我们可以把每个人p和d的差值提高20的偏移量,这样差值就大于0了,然后01背包凑数的过程,存下每种数的p,d属性,一个数可以由多个方式凑出来时,取p+d最大的情况,最后要找到可以被凑数来且最接近m*20的数。求具体方案就倒推一遍。

代码:

#include<iostream>
#include<string.h>
#include<vector>
#include<algorithm>
#include<stdio.h>
using namespace std;
struct ac{
    int p,d,w,id;
}a[210];
struct de{
    int p,d,f;
}f[210][21][810];
vector<int> v;
int main(){
    int n,m,cas=1;
    while(1){
        cin>>n>>m;
        if(n==0&&m==0) break;
        for(int i=1;i<=n;++i){
            cin>>a[i].p>>a[i].d;
            a[i].w=a[i].p-a[i].d+20,a[i].id=i;
        }
        int gal=m*20;
        v.clear();
        memset(f,0,sizeof f);
        f[0][0][0].f=1;f[0][0][0].p=f[0][0][0].d=0;
        for(int i=1;i<=n;++i){
            int w=a[i].w,p=a[i].p,d=a[i].d;
            memcpy(f[i],f[i-1],sizeof f[i]);
            for(int j=m;j>=1;--j){
                for(int k=800;k>=w;--k){
                    if(f[i-1][j-1][k-w].f&&!f[i][j][k].f) {
                        f[i][j][k].f=1;
                        f[i][j][k].p=f[i-1][j-1][k-w].p+p;
                        f[i][j][k].d=f[i-1][j-1][k-w].d+d;
                    }
                    else if(f[i-1][j-1][k-w].f&&f[i][j][k].f &&f[i-1][j-1][k-w].p+f[i-1][j-1][k-w].d+p+d>f[i][j][k].p+f[i][j][k].d){
                        f[i][j][k].p=f[i-1][j-1][k-w].p+p;
                        f[i][j][k].d=f[i-1][j-1][k-w].d+d;
                    }
                }
            }
        }
        int mn=1000000,ap=0,ad=0;
        for(int i=800;i>=0;--i) {
            if(f[n][m][i].f&&( abs(i-gal)<abs(mn-gal)||( abs(i-gal)==abs(mn-gal) && f[n][m][i].p+f[n][m][i].d>ap+ad  )   ) ){
                mn=i;
                ap=f[n][m][i].p;
                ad=f[n][m][i].d;
            }
        }
         printf("Jury #%d
",cas++);
        printf("Best jury has value %d for prosecution and value %d for defence:
",ap,ad);
        int i=n,j=m;
        while((ap||ad)&&i){
            for(int k=0;k<=800;++k){
                if(f[i-1][j-1][k].f&&ap==f[i-1][j-1][k].p+a[i].p && ad==f[i-1][j-1][k].d+a[i].d){
                    ap-=a[i].p,ad-=a[i].d;
                    v.push_back(i);
                    --j;
                    break;
                }
            }
            --i;
        }
        reverse(v.begin(),v.end());
        for(int i=0;i<v.size();++i)  printf(" %d",v[i]);
        printf("

");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/jjl0229/p/12691639.html