poj-1015

#include <stdio.h>
#include <math.h>
#include <malloc.h>

struct candicate {
    int D;
    int P;
};

struct DPstatus {
    short maxSum;
};

typedef struct candicate candicate;
typedef struct DPstatus DPstatus;

void getJury(candicate * candicateInfo, int candicateNum, int juryNum) {

    DPstatus DPmatrix[201][21][801] = {0};

    for (int i = 0; i < 201; i++) {
        for (int j = 0; j < 21; j++) {
            for (int k = 0; k < 801; k++) {
                DPmatrix[i][j][k].maxSum = -1;
            }
        }
    }

    for (int i = candicateNum; i >= 0; i--) {
        for (int j = 0; j <= juryNum; j++) {
            for (int k = -20*juryNum; k <= 20*juryNum; k++) {
                // printf("%d %d %d\n", i, j, k);
                int kPlus = 20*juryNum;
                if (j == 0 && k == 0) {   // no choose any jury member, just 0.
                    DPmatrix[i][j][k + kPlus].maxSum = 0;
                    // printf("E %d %d %d 0\n", i, j, k);
                    continue;
                } else if (j == 0 && k != 0) { // no choose one,
                    DPmatrix[i][j][k + kPlus].maxSum = -1;
                    continue;
                } else if (i == candicateNum && j == 0 && k != 0) {
                    DPmatrix[i][j][k + kPlus].maxSum = -1;
                    continue;
                } else if (i == candicateNum && j == 0 && k == 0) {
                    DPmatrix[i][j][k + kPlus].maxSum = 0;
                    // printf("E %d %d %d 0\n", i, j, k);
                    continue;
                } else if (i == candicateNum && j > 0) {
                    DPmatrix[i][j][k + kPlus].maxSum = -1;
                    continue;
                } else if (k == -20*juryNum && j < juryNum) {
                    DPmatrix[i][j][k + kPlus].maxSum = -1;
                    // printf("F1 %d %d %d 0\n", i, j, k);
                    continue;
                } else if (k == 20*juryNum && j < juryNum) {
                    DPmatrix[i][j][k + kPlus].maxSum = -1;
                    // printf("F2 %d %d %d 0\n", i, j, k);
                    continue;
                } else {
                    if (DPmatrix[i+1][j][k + kPlus].maxSum == -1 &&
                        DPmatrix[i+1][j-1][k - ((candicateInfo[i].D - candicateInfo[i].P)) +kPlus].maxSum == -1) {
                        // printf("A %d %d %d -1\n", i, j, k);
                        DPmatrix[i][j][k+kPlus].maxSum = -1;
                    } else if (DPmatrix[i+1][j][k+kPlus].maxSum == -1) { //choose this one.
                        DPmatrix[i][j][k+kPlus].maxSum = (candicateInfo[i].D + candicateInfo[i].P) + DPmatrix[i+1][j-1][k - ((candicateInfo[i].D - candicateInfo[i].P)) + kPlus].maxSum;
                        // printf("B choose %d %d %d %d  D %d P %d \n", i, j, k, DPmatrix[i][j][k+kPlus].maxSum, candicateInfo[i].D, candicateInfo[i].P);
                    } else if (DPmatrix[i+1][j-1][k - ((candicateInfo[i].D - candicateInfo[i].P)) + kPlus].maxSum == -1) {
                        DPmatrix[i][j][k+kPlus].maxSum = DPmatrix[i+1][j][k + kPlus].maxSum;
                        // printf("C no choose %d %d %d %d\n", i, j, k, DPmatrix[i][j][k+kPlus].maxSum);
                    } else {
                        int chooseSum = (candicateInfo[i].D + candicateInfo[i].P) + DPmatrix[i+1][j-1][k - ((candicateInfo[i].D - candicateInfo[i].P)) +kPlus].maxSum;
                        int noChooseSum = DPmatrix[i+1][j][k +kPlus].maxSum;
                        if (chooseSum >= noChooseSum) { // choose
                            DPmatrix[i][j][k+kPlus].maxSum = chooseSum;
                            // printf("D choose %d %d %d %d\n", i, j, k, DPmatrix[i][j][k+kPlus].maxSum);
                        } else {
                            DPmatrix[i][j][k+kPlus].maxSum = noChooseSum;
                            // printf("D no choose %d %d %d %d\n", i, j, k, DPmatrix[i][j][k+kPlus].maxSum);
                        }
                    }
                }
            }
        }
    }



    int middleZero = 20*juryNum;
    int bigEnd = 40*juryNum;

    int i = 0;
    int finalRes = 402;
    while(1) {
        if (middleZero - i == -1 || middleZero + i == bigEnd + 1) {
            break;
        }
        int left = DPmatrix[0][juryNum][middleZero - i].maxSum;
        int right = DPmatrix[0][juryNum][middleZero + i].maxSum;
        if (left >= 0 && right >= 0) {
            if (left >= right) {
                // printf("V %d %d\n", -i, left);
                finalRes = -i;
                break;
            } else {
                // printf("V %d %d\n", i, right);
                finalRes = i;
                break;
            }
        } else if (left >= 0) {
            // printf("V %d %d\n", -i, left);
            finalRes = -i;
            break;
        } else if (right >= 0) {
            // printf("V %d %d\n", i, right);
            finalRes = i;
            break;
        }
        i++;
    }

    i = 0;
    int j = juryNum;
    int k = middleZero + finalRes;
    // printf("%d\n", DPmatrix[i][j][k].maxSum);
    int DSum = 0;
    int PSum = 0;
    int chooseArray[20] = {0};
    while(1) {
        if (j == 0 || i == candicateNum) {
            break;
        }
        if (DPmatrix[i][j][k].maxSum == DPmatrix[i+1][j][k].maxSum) { // no choose this one
            // printf("%d is not choosed: D: %d P: %d  %d\n", i, candicateInfo[i].D, candicateInfo[i].P, DPmatrix[i+1][j][k].maxSum);
        } else if (DPmatrix[i][j][k].maxSum ==
                    (candicateInfo[i].D + candicateInfo[i].P) + DPmatrix[i+1][j-1][k - (candicateInfo[i].D - candicateInfo[i].P)].maxSum) { // choose this
            // printf("%d is choosed: D: %d P: %d\n", i, candicateInfo[i].D, candicateInfo[i].P);
            // printf("%d ", i + 1);
            chooseArray[juryNum-j] = i + 1;
            DSum += candicateInfo[i].D;
            PSum += candicateInfo[i].P;
            j--;
            k -= (candicateInfo[i].D - candicateInfo[i].P);
        }
        i++;
    }
    printf("Best jury has value %d for prosecution and value %d for defence:\n", DSum, PSum);
    for (int i = 0; i < juryNum; i++) {
        printf(" %d", chooseArray[i]);
    }
    printf("\n");
}

int main() {
    int JuryId = 1;
    while(1) {
        int candicateNum;
        int juryNum;
        scanf("%d %d", &candicateNum, &juryNum);
        if (candicateNum == 0 && juryNum == 0) {
            return 0;
        }
        candicate * candicateInfo = (candicate *)malloc(sizeof(candicate) * candicateNum);
        int min = 100;
        for (int i = 0; i < candicateNum; i++) {
            int D, P;
            scanf("%d %d", &((candicateInfo+i)->D), &((candicateInfo+i)->P));
        }

        printf("Jury #%d\n", JuryId);
        getJury(candicateInfo, candicateNum, juryNum);
        JuryId++;
    }

}



该题其实本质上是一个背包问题, 只不过题目描述上让人不会向那方面想, 并且, 跟DP的学术派描述也不是很直接的对应,

在这道题上阻了挺长时间的, 最后用了最基本的三维数组干过去了(其实可能降为二维的, 以后要再搞一遍二维的)。

因为是三维数组, 所以里面存maxsum的是short, 变成int就直接栈溢出了。

上面解法的出发点是对于每一个可能的DP差进行处理, 题目给了限制条件, 可选者最多200个, 最多20个陪审团员, 每个人的DP差在-20 ~ 20, 那么对于所有情况而言,

DP差的范围在 -20 * 20 ~ 20*20, 而为了用数组进行保存, 那么就需要把 ~400 ~ 400 全部加上 400 变为非负数(一个常用办法)。

那么可以定义 MAXSUM[i][j][k]  为 从candidate的第 i个位置开始 选取 j 个 陪审团员, 是的 辩控差等于k的情况下, 能得到的最大辩控和.

一开始, 要将这个三维数组全部初始化为 -1(-1 代表着 这种条件下无解, 能用-1表示是因为我们会为每个k的取值加上400, 所以只要有解, 那么必然是>=0

(=0 可能是没有选取 或者是 本身D和P就是0)的)

对于这种数组解法, 最重要的有两个:

1. 确定边界值:

<1> j == 0, k == 0, 及不选取任何的陪审团成员(简称J), 而且辩控差是0, 这种情况下, 因为没有选取任何的陪审团, 因此辩控差自然为0, 而辩控和也为0.

<2> j ==0 , k !=0, 不选取任何的陪审团成员, 但是辩控差却不是0, 这种情况下, 产生了矛盾, 因此该项为-1, 代表无解。

<3> i == candidateNum, j == 0, k == 0,  已经将备选者全部筛选完了, 其他同<1>

<4> i == candidateNum, j == 0, k != 0, 已经将备选者全部筛选完了, 其他同<2>

<5> i == candidateNum, j > 0, 已经将备选者筛选完了,但是陪审团没凑齐, 那么无解.

<6> k < -20*juryNum,  j <= juryNum,  这种情况下, 因为在j <= juryNum的情况下, 能拿到的最小的DP控辩差就是 20*juryNum, 因此无解

<7> k >20*juryNum, j <= juryNum 同<7>, 不过换成了最大的控辩差. 无解


2. 确定递推关系:

MAXSUM[i][j][k] 由两个值决定 :

<1> MAXSUM[i+1][j][k]: 第 i 个候选者不被选入陪审团, 那么控辩和就等于 从第 i+1个候选者开始进行选择, 选取 j 个人, 并且辩控差等于k.

<2> MAXSUM[i+1][j-1][k - (A[i].D - A[i].P)], 选择第i个候选者进入陪审团, 那么控辩和就等于 A[i].D + A[i].P, 再加上, 从第i个候选者开始选择, 选取 j-1个人, 并且控辩差为

k - (A[i].D - A[i].P).


如果上面两个值都是 -1(无解), 那么本值也无解, 如果只有一个有解, 那么就只能选择有效的解。

如果两个都有解, 那么从中选择最大的(因为题要求控辩和最大)。


根据这个关系, 也可以推导出三重循环(i,j  ,k)的遍历顺序, 因为 i 由 i+1 影响, j 由 j-1 影响, 而 k 由 k - (A[i].D - A[i].P) 影响,

那么 i 就从 最大的开始, j 从最小的开始, 而k 则无所谓, 因为虽然k 会被 k - (A[i].D - A[i].P)影响, 但是被其影响的前提是选择了 j -1, 而j-1(因为j 从最小的开始)

则在j的上一轮中已经全部得到了, 因此大小无关。


这道题最直观的递推式应该是这样的:

MINABS[i][j][k]   从第i个候选者开始选择j个人, 使得其DP辩控差的绝对值 是以 k为中心的 最小值.

但是这样就搞不出递推关系了. 


这道题难不在于动态规划, 而在于能想到以背包问题的思路来求解, 一般的直观反应可能是求最小的辩控差绝对值的子问题,

而想不到其实是在某一个辩控差为前提下, 求解可能的最大辩控和. (这样就和背包问题一样了, 有了两个约束条件, 选择人数 和 辩控差)。

如果题目要求的是辩控差(不是绝对值)最大 或者 最小, 那么等于约束条件只有一个, 就不再需要按照背包问题的思路来求,按照学术派的动规来求即可。

而再引入绝对值最小以后, 父问题与子问题之间的联系被断开了, 及按照常规思路的最优子结构被破坏了。(这一段有待商榷)


还有有时候正确的解可能由多组, 随便输出一组就可以AC。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值