题解 洛谷P1833 【樱花】

传说中的完全背包问题!!!

首先给大家看一下我的结构体,不然后期程序看不懂:

struct tree
{
    int t,c,p;
};
tree a[10001];
int s[10001][1001];

 

t是耗费时间,c是美学值,p是次数

其实本题跟普通的01背包差别并不大。 只不过需要加一个像下面一样的判断:

for(k=1;k<=x;++k)
s[i][j]=max(s[i][j],s[i][j-a[i].t*k]+a[i].c*k);

 

其实就是将1到最多次数的所有情况遍历一遍。s是我储存DP结果的数组,而a就是储存每棵树的数据的数据,而x就是每棵树最多能看几次。

这时候你们是不是想问:那么无限次的怎么办呢? 其实也只需要一个判断,因为看得再多次,也不可能超出题目给出的时间限制。 也就是说每棵树都有一个最大观赏次数,而它就是极限次数:(总时间/当前树观赏一次的时间)和(题目给出次数)中小的那个

x=min(a[i].p,j/a[i].t);

 

但其实可以在节约一下空间,也就是DP可以重复利用空间,使空间的利用率最大。 从而就变成了这样:

for(i=1;i<=n;++i)
    {
        for(j=t;j>=a[i].t;--j)
        {
            x=min(a[i].p,j/a[i].t);
            for(k=1;k<=x;++k)
            s[j]=max(s[j],s[j-a[i].t*k]+a[i].c*k);
        }
    }

 

记得要倒序DP,因为前面的DP结果会影响后面的,如果先替换了,就会导致DP结果错误。

但是,我们发现,其实for(k=1;k<=x;++k)一直是在与自己进行DP,也就是说,只要我们略微修改一下状态转移方程,就可以将3重循环变为2重循环:

for(i=1;i<=n;++i)
    {
        if(a[i].p==0)
        for(j=a[i].t;j<=t;++j)
        {
            s[j]=max(s[j],s[j-a[i].t]+a[i].c);
        }
        else
        for(j=t;j>=a[i].t;--j)
        {
            for(k=1;k<=a[i].p;++k)
            s[j]=max(s[j],s[j-a[i].t*k]+a[i].c*k);
        }
    }

 

只需判断其是否是无限就行了。

所以将上述结构拼凑在一起就可以AC了。

2018-03-21

 

转载于:https://www.cnblogs.com/Point-King/p/9740802.html

你可能感兴趣的:(题解 洛谷P1833 【樱花】)