POJ1015陪审团(Jury Compromise)——dp+路径记录

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

差值是有后效性的,所以“转化为可行性”,开一维记录“能否达到这个差值”。

当然可以开两维分别记录 a 和 b,但 “值只是0或1” 和 “当前元素对应一个 a 只有一个 b ,其他 b 就浪费了” 这两点说明这种状态有优化空间。

开一维记录差值,d数组的值记录最大的 a+b 值较好。

虽然差值是绝对值,但需要分正负以计算。可以全部加一个修正值 fix 解决脚标非负的问题。(不要试图开一维0或1记录正负,会在边界的地方分类讨论而死)

然后是重点!!!如何记录路径???

用了LICS记录路径的自己的那个方法:记一个pre,记一个use。但本题中的意义与那题略有不同。自我感觉还不错。虽然慢到了469ms。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int fix=405;
int n,m,a[205],b[205],d[25][815],pre[205][25][815],ls,xnt,use[205][25][815];
void print(int i,int j,int k,int as,int bs)
{
    if(!j)
    {
        printf("Best jury has value %d for prosecution and value %d for defence:\n",as,bs);
        return;
    }
    print(pre[i][j][k],j-1,k-(a[i]-b[i]),as+a[i],bs+b[i]);//pre只存了阶段,j和k还得自己传参 
    printf("%d ",i);
}
int main()
{
    while(++xnt)
    {
        scanf("%d%d",&n,&m);
        if(!n&&!m)return 0;
        const int lm=20*m;
        for(int i=1;i<=n;i++)
            scanf("%d%d",&a[i],&b[i]);
        memset(d,-2,sizeof d);
        memset(pre,0,sizeof pre);
        d[0][fix]=0;
        for(int i=1;i<=n;i++)
        {
            memcpy(use[i],use[i-1],sizeof use[i-1]);//如果什么都不做,就是沿用着i-1的值(省去的那一维) 
            memcpy(pre[i],pre[i-1],sizeof pre[i-1]);//pre记录上一步是哪个阶段 
            for(int j=m;j;j--)                            //(对于没有用当前的i的情况,没什么用)
            {
                int k=a[i]-b[i];
                for(int l=-lm+fix;l<=lm+fix;l++)
                    if(l-k>=-lm+fix&&l-k<=lm+fix&&d[j-1][l-k]+a[i]+b[i]>d[j][l])
                    {
                        d[j][l]=d[j-1][l-k]+a[i]+b[i];
                        use[i][j][l]=i;//在i这个阶段,达到j和l的是第i号元素 
                        if(use[i-1][j-1][l-k]==i-1)        //第几个阶段就是第几号元素。因第i阶段正在考虑第i号元素 
                            pre[i][j][l]=i-1;
                        else pre[i][j][l]=use[i-1][j-1][l-k];//暨第几阶段 
                    }
            }            //use和pre的分离只是为了那个判断,能在被用的元素处pre停留一下 
        }                //但他们的意义还是有不同的 
            
        for(int i=fix,j=fix;i<=lm+fix&&j>=-lm+fix;i++,j--)
            if(d[m][i]>=0||d[m][j]>=0)
            {
                if(d[m][i]>=0)ls=i;
                if(d[m][j]>d[m][i])ls=j;
                break;
            }
        printf("Jury #%d\n",xnt);
        if(use[n][m][ls]==n)
        {
            print(pre[n][m][ls],m-1,ls-(a[n]-b[n]),a[n],b[n]);
            printf("%d ",n);
        }
        else print(use[n][m][ls],m,ls,0,0);//不是pre!! 
        printf("\n");
    }
}

然而有100ms左右的方法,还有各种各样的:http://www.cnblogs.com/Zinn/p/8571061.html

记录路径是一门学问。

posted on 2018-03-15 00:32  Narh  阅读(96)  评论(0编辑  收藏  举报

导航