poj 1015 动态规划

题意:从 n (1<=n<=200)个候选人中选 m (1<=m<=20)人组成陪审团。控方和辩方给所有候选人打分D[i]和P[i],分值从0 到20。

为了公平起见,法官选出陪审团的原则是:选出的m 个人,必须满足辩方总分D和控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的 |D-P| 值相同,那么选辩控双方总分之和D+P最大的方案。

分析:用dp(i,j)表示取i个候选人,差为j的总和。则转移函数为 dp(i, j) = max dp(i-1, j-Vt), (t在i-1个人中没被选到,Vt=Dt-Pt)。

此题要记录选了哪些人,用path[i][j]表示dp[i][j]最后选的那个人的编号,假设为t,则倒数第二个人为 path[i-1][j-Vt]。

另外,由于差值 j 可能为负,所以一律加上400(最多20人,每人最小-20分,所以最小为-400)。

 

 

const int N = 202, M = 22, K = 802, fix = 400;
int n, m, ii, jj, T;
int p[N], d[N], v[N], s[N];//v差 s和
int dp[M][K], path[M][K];//dp[i][j]选i个人,差为j的最大和  path[i][j]为第i个人的编号
int b[N];

void check(int i, int j){
    FOE(t, 1, n) b[t] = 0;
    while(i){
        int t = path[i][j];
        b[t] = 1;
        j = j - v[t];   i--;
    }
}

void read(){
    FOE(i, 1, n) {
        scanf("%d%d", &p[i], &d[i]);
        v[i] = d[i]-p[i]; s[i] = p[i]+d[i];
    }
    ii = jj = -1;
}

void DP(){
    memset(dp, -1, sizeof dp);
    dp[0][fix] = 0;
    FOR(i, 0, m) FOE(j, 0, 800){ if(dp[i][j] == -1) continue;
        check(i, j);//找出dp[i][j]没选的人
        FOE(t, 1, n){ if(b[t]) continue;
            int x = i +1, y = j + v[t];//更新d[i+1][j+v[t]]
            if(dp[x][y] == -1 || dp[x][y] < dp[i][j] + s[t]) {
                dp[x][y] = dp[i][j] + s[t];
                path[x][y] = t;

                if(x==m) {//更新最优解
                    int t1 = abs(jj-fix), t2 = abs(y-fix);
                    if(jj==-1 || t1>t2 || (t1==t2 && dp[ii][jj]<dp[x][y])){
                        ii = x; jj = y;
                    }
                }
            }
        }
    }
}

void print(){
    int dd = (dp[ii][jj] + jj - fix)/2;
    int pp = dp[ii][jj] - dd;
    FOE(t, 1, n) b[t] = 0;
    while(ii){
        int t = path[ii][jj]; b[t] = 1;
        jj = jj - v[t];   ii--;
    }
    printf("Jury #%d \n", ++T);
    printf("Best jury has value %d for prosecution and value %d for defence: \n", pp, dd);
    FOE(t, 1, n) if(b[t]) printf(" %d", t);
    printf("\n");
}

int main(){
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    #endif

    while(scanf("%d%d", &n, &m), n){
        read();
        DP();
        print();
    }

    return 0;
}

 

 

posted @ 2013-05-28 18:47  心向往之  阅读(170)  评论(0编辑  收藏  举报