POJ 1015--Jury Compromise

题意

题目大意是讲,在遥远的国家佛罗布尼亚,嫌犯由陪审团决定是否有罪。先随机挑选n个人作为陪审团的候选人,然后再从这n个人中选m人组成陪审团。控方和辩方会给所有候选人打分,分值从0到20(分数越高代表越喜欢)。为了公平起见,选出的m个人必须满足辩方总分和控方总分之差的绝对值最小。如果出现多种方案,那么选辩控双方总分之和最大的方案即可。

分析

比较容易想到的就是动态规划,先对候选人进行排序,由于评分差为-20到20,将评分差加上20,变为0到40。用一个二维数组表示选中的第几号人v[index][value], 假定评分数组为diff[] = {10, 20, 23, 29…},比如v[1][20]==2,代表选第一个人时,评分为20时,选中了第2号人。以此类推v[2][43]==3,这样可以得到一个完整的选择链(想要知道前一个人的选择,只需求v[2 - 1][43 - diff[3]] = v[1][20] = 2)。

最后,假定需要选择10个人,若v[10][400] != 0,那么v[10][400]下面的选择链便是最优解。反之搜索最靠近v[10][400]的情况即可。比如当v[10][390] != 0 && v[10][405] != 0,那么v[10][405]必定优于v[10][390]。事实上,这种方法会有重复计算的情况,再加优化的话,100MS内应该没什么问题。

代码如下:
Memory: 328K Time: 250MS Length:154LINES

#include<iostream>
#include<cstring>
using namespace std;
struct JURY                           //陪审员
{
    int count;                    //原始编号
    int pi;
    int di;
};
int v[21][801] = {};
JURY pJury[200];
inline int inline_sum(JURY& j) { return j.di + j.pi; }
inline int inline_diff(JURY& j) { return j.pi - j.di + 20; }
void Sort_Insert(const int c)          //插入排序
{
    for (int i = 1; i < c; ++i)
    {
        JURY temp = pJury[i];
        int j = i;
        while (j > 0)
            if (inline_diff(temp) < inline_diff(pJury[j - 1]))
            {
                pJury[j] = pJury[j - 1];
                --j;
            }
            else    break;
            pJury[j] = temp;
    }
}
void Find_min_max(const int c, int count, int& minval, int& maxval)  //计算前后count个候选人辨控差的和
{
    for (int i = 0; i < count; ++i)
    {
        minval += inline_diff(pJury[i]);
        maxval += inline_diff(pJury[c - 1 - i]);
    }
}
int Update(int depth, int value, int tmpvalue)                    //更新选择链
{
    int cu = 0;
    int pen = 0;
    while (depth > 0)
    {
        cu += inline_sum(pJury[v[depth][value]]);
        pen += inline_sum(pJury[v[depth][tmpvalue]]);
        value -= inline_diff(pJury[v[depth][value]]);
        tmpvalue -= inline_diff(pJury[v[depth][tmpvalue]]);
        --depth;
    }
    return pen - cu;
}
bool find_pos(int depth, int value, int k)                    //在一个选择链上查找某个候选人是否已经存在
{
    while (depth > 0)
    {
        if (v[depth][value] == k)
            return true;
        value -= inline_diff(pJury[v[depth][value]]);
        --depth;
    }
    return false;
}
inline int inline_max(int a, int b) { return inline_sum(pJury[a]) > inline_sum(pJury[b]) ? a : b; }
int main()
{
    int candidates = 0;
    int members = 0;
    int count = 0;
    while (cin >> candidates >> members && candidates != 0 && members != 0)
    {
        int selected[200] = {};
        memset(&v, -1, sizeof(v));
        for (int i = 0; i < candidates; ++i)
        {
            cin >> pJury[i].pi >> pJury[i].di;
            pJury[i].count = i;
        }
        Sort_Insert(candidates);
        for (int j = 1; j <= members; ++j)
        {
            int maxval = 0;
            int minval = 0;
            Find_min_max(candidates, j, minval, maxval);
            for (int i = minval; i <= maxval; ++i)
            {
                for (int k = 0; k < candidates - members + j; ++k)
                {
                    if (i < inline_diff(pJury[k]));
                    else if (i == inline_diff(pJury[k]) && j == 1)          v[j][i] = v[j][i] != -1 ? inline_max(v[j][i], k) : k;
                    else if (v[j - 1][i - inline_diff(pJury[k])] != -1)
                    {
                        if (v[j][i] == -1) //无值时,先搜索是否已存在
                        {
                            if (!find_pos(j - 1, i - inline_diff(pJury[k]), k))     v[j][i] = k;
                        }
                        else     //有值时,先搜索,再比较
                        {
                            if (!find_pos(j - 1, i - inline_diff(pJury[k]), k))
                                if (inline_sum(pJury[k]) - inline_sum(pJury[v[j][i]]) + Update(j - 1, i - inline_diff(pJury[v[j][i]]), i - inline_diff(pJury[k])) > 0)
                                    v[j][i] = k;
                        }
                    }
                }
            }
        }
        int v1, v2, val;
        val = v1 = v2 = members * 20;
        if (v[members][v1] == -1)              //寻找最优解
        {
            while (v1 <= members * 40)
            {
                ++v1;
                --v2;
                if (v[members][v1] == -1 && v[members][v2] != -1)
                {
                    val = v2;
                    break;
                }
                else if (v[members][v1] != -1 && v[members][v2] == -1)
                {
                    val = v1;
                    break;
                }
                else if (v[members][v1] != -1 && v[members][v2] != -1)
                {
                    if (inline_sum(pJury[v[members][v1]]) - inline_sum(pJury[v[members][v2]]) + Update(members - 1, v2 - inline_diff(pJury[v[members][v2]]), v1 - inline_diff(pJury[v[members][v1]])) > 0)
                        val = v1;
                    else            val = v2;
                    break;
                }
            }
        }
        int sum_pi = 0;
        int sum_di = 0;
        for (int i = members; i > 0; --i)
        {
            selected[pJury[v[i][val]].count] = 1;
            sum_pi += pJury[v[i][val]].pi;
            sum_di += pJury[v[i][val]].di;
            val -= inline_diff(pJury[v[i][val]]);
        }
        cout << "Jury #" << ++count << endl;
        cout << "Best jury has value " << sum_pi << " for prosecution and value " << sum_di << " for defence:" << endl;
        int mm = 0;
        for (int i = 0; i < candidates && mm < members; ++i)
            if (selected[i])
            {
                cout << " " << i + 1;
                ++mm;
            }
        cout << endl << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值