DP<背包?> POJ 1015

这道题我都不想写解题报告了..因为是看了一个很漂亮的acmer的解题报告写的~~


然后她写嘚很清楚吖~~~


真心佩服..

题意就是:

在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定。陪审团是由法官从公众中挑选的。先随机挑选n 个人作为陪审团的候选人,然后再从这n 个人中选m 人组成陪审团。选m 人的办法是:控方和辩方会根据对候选人的喜欢程度,给所有候选人打分,分值从0 到20。为了公平起见,法官选出陪审团的原则是:选出的m 个人,必须满足辩方总分D控方总分P的差的绝对值|D-P|最小。如果有多种选择方案的 |D-P| 值相同,那么选辩控双方总分之和D+P最大的方案即可。

输出:

选取符合条件的最优m个候选人后,要求输出这m个人的辩方总值D和控方总值P,并升序输出他们的编号。


思路:

主要是用了两个数组:

dp[i][k]..       记录当选了 i 个人..辩控差是k的时候..辩控和最大值..

path[i][k]..     记录当选了 i 个人..辩控差是k..然后对应dp数组值<辩控和最大>的前一个人的编号..


注意因为是靠遍历所有点..然后找满足条件<dp[i-1][k] +  pre[j].s > dp[i][k+pre[j].v]> 的 k 值..所以k值一定要是正数..所以就要弄一个修正值fix = 20*m..<因为当辩方值为0 控方值为 20的时候..和就是-20*m了..>


初始条件是当 i = 0    k就是0了..  这时候dp[i][k] = 0

然后主要三层循环是:

第一层.. dp 中的 i 从0 - m..<确定m个人的 dp 数组值..>

第二层.. dp 中的 k 从0-fix*2..<看看有没有当 i 值确定情况下符合条件的 k 值..     到 fix*2 的原因是fix 是等同于 0 >

       <找满足条件的状态..>

第三层..遍历所有的點..查找满足条件的點..

当找到可以dp[i-1][k] +  pre[j].s > dp[i][k+pre[j].v] 的點..并且没有加入审判团的~

就更改状态并更改相应路径..


感觉主要难确定的是每一维代表的变量..

Code:

#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;

struct Inf
{
    int p;  ///控方值
    int d;  ///辩方值
    int s;  ///控辩和
    int v;  ///控辩差
}pre[210];

int path[25][810];//记录所选定的候选人的编号  
int dp[25][810];//dp[j][k]:取j个候选人,使其辩控差为k的所有方案中,辩控和最大的方案的辩控和  

/*回溯,确认dp[j][k]方案是否曾选择过候选人i*/  
bool select(int j, int k, int i)
{
    while(j > 0 && path[j][k] != i)
    {
        k -= pre[path[j][k]].v;
        j--;
    }
    if(j == 0) return true;
    else return false;
}

int main()
{
    int id[25];
    int i, j, k;
    int n, m;
    int T = 0;
    while(scanf("%d %d", &n, &m) != EOF)
    {
        if(n == 0 && m == 0) break;
        memset(path, 0, sizeof(path));
        memset(pre, 0, sizeof(pre));
        memset(dp, -1, sizeof(dp));///一开始定义为-1..表示该方案不可行..以后状态转移找可行方案的时候用到..

        for(i = 1; i <= n; ++i){
            scanf("%d %d", &pre[i].p, &pre[i].d);
            pre[i].s = pre[i].p + pre[i].d;
            pre[i].v = pre[i].p - pre[i].d;
        }

        int fix = m*20;//总修正值,修正极限为从[-400,400]映射到[0,800]  
        dp[0][fix] = 0; //由于修正了数值,因此dp[0][fix]才是真正的dp[0][0]  

		/*DP*/  
        for(i = 1; i <= m; ++i)
        for(k = 0; k <= fix*2; ++k)
            if(dp[i-1][k] >= 0)
                for(j = 1; j <= n; ++j)
                if(dp[i-1][k] +  pre[j].s > dp[i][k+pre[j].v] && select(i-1, k, j)){///!!!!Important~ select d+p is max while the solution is d-p
                    dp[i][k+pre[j].v] = dp[i-1][k] + pre[j].s;
                    path[i][k+pre[j].v] = j;
                 }

        int k;
        for(k = 0; k <= fix; ++k){
            if(dp[m][fix-k] >= 0 || dp[m][fix+k] >= 0) ///promise the d-p is min
            ///!!!!//从中间向两边搜索最小辨控差的位置k  
            break;
        }

        int div = dp[m][fix-k] > dp[m][fix+k]?(fix-k):(fix+k);//最小辨控差  

		/*Output*/  
        int D = (dp[m][div] - div + fix)/2;   //辩方总值 = (辨控和+辨控差-修正值)/2  
        int P = (dp[m][div] + div - fix)/2;  //控方总值 = (辨控和-辨控差+修正值)/2  

        printf("Jury #%d\n", ++T);
        printf("Best jury has value %d for prosecution and value %d for defence:\n", P, D);

        for(i = 0, j = m, k = div; i < m; ++i){
            id[i] = path[j][k];
            k -= pre[id[i]].v;
            j--;
        }

        sort(id, id+m);

        for(i = 0; i < m; ++i)
        printf(" %d", id[i]);
        printf("\n\n");
    }

    return 0;
}

那个漂亮的acmer写嘚很好吖~所以这里就不过多解释了..


贴上漂亮acmer的解题报告~我也要像她那么厉害~~~

YoU   http://blog.csdn.net/lyy289065406/article/details/6671105

Stkcd [股票代码] ShortName [股票简称] Accper [统计截止日期] Typrep [报表类型编码] Indcd [行业代码] Indnme [行业名称] Source [公告来源] F060101B [净利润现金净含量] F060101C [净利润现金净含量TTM] F060201B [营业收入现金含量] F060201C [营业收入现金含量TTM] F060301B [营业收入现金净含量] F060301C [营业收入现金净含量TTM] F060401B [营业利润现金净含量] F060401C [营业利润现金净含量TTM] F060901B [筹资活动债权人现金净流量] F060901C [筹资活动债权人现金净流量TTM] F061001B [筹资活动股东现金净流量] F061001C [筹资活动股东现金净流量TTM] F061201B [折旧摊销] F061201C [折旧摊销TTM] F061301B [公司现金流1] F061302B [公司现金流2] F061301C [公司现金流TTM1] F061302C [公司现金流TTM2] F061401B [股权现金流1] F061402B [股权现金流2] F061401C [股权现金流TTM1] F061402C [股权现金流TTM2] F061501B [公司自由现金流(原有)] F061601B [股权自由现金流(原有)] F061701B [全部现金回收率] F061801B [营运指数] F061901B [资本支出与折旧摊销比] F062001B [现金适合比率] F062101B [现金再投资比率] F062201B [现金满足投资比率] F062301B [股权自由现金流] F062401B [企业自由现金流] Indcd1 [行业代码1] Indnme1 [行业名称1] 季度数据,所有沪深北上市公司的 分别包含excel、dta数据文件格式及其说明,便于不同软件工具对数据的分析应用 数据来源:基于上市公司年报及公告数据整理,或相关证券交易所、各部委、省、市数据 数据范围:基于沪深北证上市公司 A股(主板、中小企业板、创业板、科创板等)数据整理计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值