0-1背包(SOJ 2142)

SOJ 2142: Cow Exhibition

同样的题目可见:POJ 2184

问题:给出$n$个元素$node[i],1\le i\le n$,每个元素$node[i]$有两个属性smartnessfunness.现在从这$n$个元素中找出一些元素$node[k], k\in S, S\subseteq[n]$使得$TS+TF$最大同时$TS\ge0, TF\ge0$,其中

$TS=\sum_{k\in S, S\subseteq[n]}node[k].smartness$,

$TF=\sum_{k\in S, S\subseteq[n]}node[k].funness$.

分析:这道题我自己想了很久没有思路,也尝试过往$0-1$背包上靠,但没想出来。后来看了网上的报告,确实是$0-1$背包模型。把smartness看作重量,把funness看作价值,定义$dp[i][j]$为使用前$i$个元素,容量为$j$时的最大funness之和,则

$dp[i][j]=\max\{dp[i-1][j],dp[i-1][j-node[i].smartness]+node[i].funness\}$.

然后经过$0-1$背包,我们得到所有$dp[j]$的值。这种做法实际上是遍历smartness之和(也即是$j$)的值,当$j$固定了之后,我们只用取对应的funness之和的最大值,也即是$dp[j]$, 然后扫一遍$j$满足$j\ge0$, $dp[j]\ge0$并且$j+dp[j]$最大即可得到最优解。

上面是核心的思想,具体的细节问题还需要注意。题目中$-1000\le smartness\le 1000$, $-1000\le funness\le 1000$, 因为smartness做背包体积,并且出现负数,所以我们令每个$smartness=smartness+1000$变成非负。这样又出现一个问题,计算完了$0-1$背包,对于某个$j$,我们需要知道它是由几个$node[i].smartness$构成的,然后令$j$减去相应个数乘以$1000$. 但是我们不知道这个个数,为了解决这个问题,我们改变$dp[i][j]$的定义为使用前$i$个元素,容量为$j$时的最大smartness和funness之和,则

$dp[i][j]=\max\{dp[i-1][j],dp[i-1][j-node[i].smartness]+node[i].smartness-1000+node[i].funness\}$.

这样我们得到的$dp[j]$为smartness之和为$j$时smartness和funness之和的最大值。为了分离出smartness之和以及funness之和,我们创建一个辅助数组$TF[j]$记录$dp[j]$对应的funness之和,这样扫描一遍$j$满足$TF[j]\ge0$, $dp[j]\ge TF[j]$并且$dp[j]$最大即可得到最优解。

代码:

#include<iostream>
using namespace std;
struct node
{
    int smartness;
    int funness;
};
node cows[105];
int dp[200005];
int TF[200005];
int main()
{
    int N;
    int i, j;
    int INF = -0x3f3f3f3f;
    int Vol;
    int temp;
    int ans;
    while (scanf("%d", &N) == 1)
    {
        Vol = 0;
        for (i = 0; i < N; i++)
        {
            scanf("%d%d", &cows[i].smartness, &cows[i].funness);
            cows[i].smartness += 1000;
            Vol += cows[i].smartness;
        }
        for (j = 1; j <= Vol; j++)
        {
            dp[j] = INF;
            TF[j] = INF;
        }
        dp[0] = 0;
        TF[0] = 0;
        for (i = 0; i < N; i++)
            for (j = Vol; j >= cows[i].smartness; j--)
                if (dp[j - cows[i].smartness] > INF)
                {
                    temp = dp[j - cows[i].smartness] + cows[i].funness + cows[i].smartness - 1000;
                    if (temp>dp[j])
                    {
                        dp[j] = temp;
                        TF[j] = TF[j - cows[i].smartness] + cows[i].funness;
                    }
                }
        ans = 0;
        for (j = 0; j <= Vol; j++)
            if (TF[j] >= 0 && dp[j] >= TF[j])
                ans = dp[j] > ans ? dp[j] : ans;
        printf("%d\n", ans);
    }
    return 0;
}
View Code

参考:

https://blog.csdn.net/zhangxb35/article/details/39207923

posted on 2019-03-20 11:41  小叶子曰  阅读(155)  评论(0编辑  收藏  举报

导航