poj 1015 陪审团问题

/*
*              poj 1015 经典二维费用满背包问题 by baiwenlei
*
* 题目大意: 从n个人中选出m个作为陪审团成员。选举规则: 控方和辩方分别对n个人打分(0~20), 选出m个人
*           使得m个人的控方得分总和PJ与辩方得分总和DJ之差的绝对值|PJ-DJ|最小, 在多套方案差值相同
*           的情况下选|DJ+PJ|最大的一组。
*
* 输入: 1<=n<=200, 1<=m<=20
*
* 解题思路: 因为得分范围有限,故而可以采用枚举法将所有DJ-PJ的可能值枚举出来。
*           
*           0. 对于每个人来说只有2种选择: 选或者不选 -- 搜索的思想
*
*           1. |PJ-DJ|差值没有最优子结构,但是|PJ+DJ|是有最优子结构的
*
*           2. n个人中选择m个最优方案组合,一看就是01背包中的一维费用

            3.填满型背包,初始化时需初始成负数,并判断是否为负数 -- 表示非法值
*
*
*/

#include <iostream>

namespace {
    using namespace std;

    const int M_MAX = 20;
    const int N_MAX = 200;
    int d[N_MAX+1], p[N_MAX+1], diff[N_MAX+1], sum[N_MAX+1];

    const int PREFER_VAL_MIN = 0;
    const int PREFER_VAL_MAX = 20;
    const int DP_DIFF_MIN = PREFER_VAL_MIN - PREFER_VAL_MAX;
    const int DP_DIFF_MAX = PREFER_VAL_MAX - PREFER_VAL_MIN;
    const int DIFF_ADJUST = DP_DIFF_MAX * M_MAX;

    int f[N_MAX+1][M_MAX+1][(DP_DIFF_MAX-DP_DIFF_MIN)*M_MAX + 1];
    bool path[N_MAX+1][M_MAX+1][(DP_DIFF_MAX-DP_DIFF_MIN)*M_MAX + 1];

    int n, m;

    void reset()
    {
        int s = m * DP_DIFF_MIN;
        int e = m * DP_DIFF_MAX;
        for (int j=0; j<=m; j++)
        {
            for (int d=s; d<=e; d++)
            {
                f[0][j][d+DIFF_ADJUST] = -1;
                path[0][j][d+DIFF_ADJUST] = false;
            }
        }
        
        f[0][0][DIFF_ADJUST] = 0;
    }
    
    int jury_compromise()
    {
        reset();

        int s = m * DP_DIFF_MIN;
        int e = m * DP_DIFF_MAX;
        for (int i=1; i<=n; i++)
        {
            for (int j=0; j<=m; j++)
            {
                for (int d=s; d<=e; d++)
                {
                    int k = d+DIFF_ADJUST;
                    
                    f[i][j][k] = f[i-1][j][k];
                    path[i][j][k] = false;
                    
                    if (j==0 || k-diff[i]<0 || k-diff[i]>800) continue;
                    
                    if (f[i-1][j-1][k-diff[i]] == -1) continue;

                    if (f[i-1][j-1][k-diff[i]] + sum[i] > f[i-1][j][k])
                    {
                        f[i][j][k] = f[i-1][j-1][k-diff[i]] + sum[i];
                        path[i][j][k] = true;
                    }
                }
            }
        }
    }

    void path_print(int deepth, int i, int j, int k)
    {
        if (deepth == m)
        {
            return;
        }

        if (path[i][j][k])
        {
            path_print(deepth+1, i-1, j-1, k-diff[i]);
            cout << " " << i;
        }
        else
        {
            path_print(deepth, i-1, j, k);
        }

        return;
    }

    void compromise_print(int nth)
    {
        int sum_max = -1;
        int diff_min = 0;

        for (int k=0; k<=m*DP_DIFF_MAX; k++)
        {
            if (f[n][m][DIFF_ADJUST+k] > sum_max)
            {
                sum_max = f[n][m][DIFF_ADJUST+k];
                diff_min = k;
            }

            if (f[n][m][DIFF_ADJUST-k] > sum_max)
            {
                sum_max = f[n][m][DIFF_ADJUST-k];
                diff_min = -k;
            }

            if (sum_max != -1)
            {
                break;
            }
        }

        int sum_d, sum_p;
        sum_d = (sum_max + diff_min)/2;
        sum_p = (sum_max - diff_min)/2;
        cout << "Jury #" << nth << endl;
        cout << "Best jury has value " << sum_p << " for prosecution"
             << " and value " << sum_d << " for defence:" << endl;

        path_print(0, n, m, DIFF_ADJUST+diff_min);

        cout << endl;
        
        return;
    }

}

int main()
{
    int nth = 1;
    while (cin>>n>>m && (n!=0 && m!=0))
    {
        for (int i=1; i<=n; i++)
        {
            cin >> p[i] >> d[i];
            diff[i] = d[i] - p[i];
            sum[i] = d[i] + p[i];
        }
        jury_compromise();
        compromise_print(nth++);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值