洛谷 P1064: [NOIP 2006 提高组] 金明的预算方案

解题过程

首先发现这是一个典型的依赖 DP,但是进一步观察可发现最多只有一层。

20pts

于是我们发现每次无非就是只买主件、买主件和第一个附件、买主件和第二个附件以及买主件和所有附件这四种可能。于是我们在每次输入时判断这个部件是不是主件,如果是就直接加入到后续的物品中,如果不是则再判断是不是第一个附件,如果是则把“买主件和第一个附件”这种情况加入到后续的物品中,如果还不是则把“买主件和第二个附件”以及“买主件和所有附件”这两种情况加入到后续的物品中。

接下去再用 01 背包即可。

点击查看代码
#include <bits/stdc++.h>
#define int long long
const int N = 2e5 + 5;
const int Mod = 1e9 + 7;
using namespace std;
int n, m;
int v[N], w[N], q[N], first[N];
int vi[N], wi[N];
int cnt;
int f[N];
signed main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> v[i] >> w[i] >> q[i];
        if (!q[i])
        {
            wi[++cnt] = v[i];
            vi[cnt] = v[i] * w[i];
        }
        else if (!first[q[i]])
        {
            first[q[i]] = i;
            wi[++cnt] = v[q[i]] + v[i];
            vi[cnt] = v[q[i]] * w[q[i]] + v[i] * w[i];
        }
        else
        {
            wi[++cnt] = v[q[i]] + v[i];
            vi[cnt] = v[q[i]] * w[q[i]] + v[i] * w[i];
            wi[++cnt] = v[q[i]] + v[first[q[i]]] + v[i];
            vi[cnt] = v[q[i]] * w[q[i]] + v[first[q[i]]] * w[first[q[i]]] + v[i] * w[i];
        }
    }
    for (int i = 1; i <= cnt; i++)
    {
        for (int j = n; j >= wi[i]; j--)
        {
            f[j] = max(f[j], f[j - wi[i]] + vi[i]);
        }
    }
    cout << f[n];
    return 0;
}

40pts

当然,这样会 WA。仔细观察,发现问题出在如果选了这四种情况中的一个,别的情况就不能选了。于是我们再改用分组背包,采用 vector 记录每个主件的所有情况。

点击查看代码
#include <bits/stdc++.h>
#define int long long
const int N = 3.2e4 + 5;
const int M = 65;
const int Mod = 1e9 + 7;
using namespace std;
int n, m;
vector<int> vi[M], wi[M];
int f[N];
signed main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int v, w, q;
        cin >> v >> w >> q;
        if (!vi[q].size())
        {
            wi[i].push_back(v);
            vi[i].push_back(v * w);
        }
        else if (vi[q].size() == 1)
        {
            wi[q].push_back(wi[q][0] + v);
            vi[q].push_back(vi[q][0] + v * w);
        }
        else
        {
            wi[q].push_back(wi[q][0] + v);
            vi[q].push_back(vi[q][0] + v * w);
            wi[q].push_back(wi[q][0] + wi[q][1] + v);
            vi[q].push_back(vi[q][0] + vi[q][1] + v * w);
        }
    }
    for (int i = 1; i <= m; i++)
    {
        for (int j = n; j >= 0; j--)
        {
            for (int k = 0; k < vi[i].size(); k++)
            {
                if (j >= wi[i][k])
                {
                    f[j] = max(f[j], f[j - wi[i][k]] + vi[i][k]);
                }
            }
        }
    }
    cout << f[n];
    return 0;
}

100pts

观察上一次的代码,发现在处理“买主件和所有附件”这种情况时会出现问题,因为在 wi[q][1] 中已经包含了 wi[q][0]vi 也一样。

删除 wi[q][0]vi[q][0] 即可。

提交记录

posted @ 2025-05-16 21:36  jackzhang2013  阅读(46)  评论(0)    收藏  举报