poj 1015 Jury Compromise dp

http://poj.org/problem?id=1015

题意:给出几对数,求出|左边总和-右边总和|的值最小,如果存在几对数,需要输出左边总和+右边总和最大的那一个。

考察点:类似背包的巧妙应用;灵活的找到入口;

提交情况:我错了好多次,有几个错误导致。第一个错误我把v[],和sum[],开小了,第2个错误是我把函数表达式写错了,导致越界(见代码);第三个错误:我把x,y的值附为0,导致对出现0,0的情况出错;

思路:一共分为一下几部

1,先寻找子问题,即dp的入口,我总是一直把入口的寻找固定为:已经找到前几个书的最优值,在依次向后退,这是递归思想太严重,(2479题也是这样的犯错),此题的入口为先设定找到第一个人(陪审团的人),同时把他们的差值和总和全部记下,即dp[i][j],i为陪审团的人数,j为他们的差值为j,可以为负,但为了存储,我们应该加上修正值500;dp[i][j]为该状态的最大值;

2,先求出dp[1][j];

3,然后求出dp[i][j],在寻找的过程中,对dp[i][j]中未记录的点(用path[i][j]表示)进行遍历,记录的点跳过;

收获:理解了解决dp题,他们的入口多种多样,此题的入口为陪审团的人数为1,2,3。。。,同时他们的最小值进行枚举;

经验:对有思路的题,我们应该坚决坚持自己写,不管自己的代码有多丑陋,同时在写代码的时候我们应该有自信,同时要考虑全面,不要像我一样,谢了这个代码犹豫了几个小时

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


int dp[35][1000];//i为陪审团的人数,j为枚举的人数为i的差值,dp[i][j]为最大的总和值
int path[35][1000];//path用来记录dp[i][j]当时的路径
int v[350];//用来记录每个点的差值
int sum[350];//用来记录每个点的总和
int a[35];//记录最优值的各点,但后排序并输出;


int main()
{
    int ans=0;
    int n,m;
    int i,j,k;
    int x,y;
    int t;
    int t1,t2;
    while(scanf("%d%d",&n,&m),(n&&m))
    {
        memset(dp,-1,sizeof(dp));
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&x,&y);
            v[i]=x-y;
            sum[i]=x+y;
            if(dp[1][v[i]+500]<sum[i])
            {
                dp[1][v[i]+500]=sum[i];
                path[1][v[i]+500]=i;
            }
        }
       //求出dp[1][j]的值
        
     for(i=2;i<=m;i++)
     {
         for(j=50;j<=1000;j++)
         {
            if(dp[i-1][j]==-1)
                continue;
            for(k=1;k<=n;k++)
            {
                t=j;
                for(x=i-1;x>=1;x--)
                {
                   //  y=dp[x][t];
                   // if(k==path[x][t-v[y]])
                    //    break;
                  //  t=t-v[y];
                  //这是刚才写的导致越界
                    if(k==path[x][t])
                        break;
                    t=t-v[path[x][t]];

                }
                if(x>=1)
                    continue;
                if(dp[i][j+v[k]]<dp[i-1][j]+sum[k])
                {
                    dp[i][j+v[k]]=dp[i-1][j]+sum[k];
                    path[i][j+v[k]]=k;
                }
             }
         }
    }
    //求出dp[i][j]的各个值,path[i][j]记录每个dp值的路径
    
    printf("Jury #%d\n",++ans);
    for(i=0;i<=500;i++)
    {
        t1=-i;
        t2=i;
        y=x=-8;
        //y=x=0,刚才写的,导致0的情况出错
        if(dp[m][500+i]!=-1)
            y=dp[m][500+i];
        if(dp[m][500-i]!=-1)
            x=dp[m][500-i];
        if(x>y)
        {
            y=x;
            t2=t1;
        }
        if(x+y>=0)
            break;
    }
    printf("Best jury has value %d for prosecution and value %d for defence:\n",(y+t2)/2,(y-t2)/2);
    y=500+t2;
    t=0;
    for(i=m;i>=1;i--)
    {
        x=path[i][y];
        a[t++]=x;
        y=y-v[x];
    }
    sort(a,a+t);
    for(i=0;i<t;i++)
        printf(" %d",a[i]);
    printf("\n");
    //按要求输出
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值