心的爱「心」午餐

心的爱「心」午餐

题目描述

虽在小镇上最受欢迎的折纸咖啡馆工作,身为料理技艺高超的老板娘的女儿,织部心的料理水平却不尽人意,她的料理能让人「深刻地感受到自己是活着的」。

一天,在诚外出采购时,心为了犒劳自己亲爱的「哥哥」,为诚做了一桌爱心午餐。

这份午餐有 $n$ 种料理。第 $i$ 种料理的美味值为 $w_i$,属性为 $c_i$。

不同料理之间的食材可能会发生相互作用。一共有 $m$ 组这样的关系。每组关系由三个整数 $(u_i,v_i,W_i)$ 描述,表示若同时吃下第 $u_i$ 种和第 $v_i$ 种料理,则会额外获得 $W_i$ 的美味值。

在心的强烈要求下,诚必须满足以下条件:

  • 至少吃掉 $k$ 种料理;
  • 每种属性的料理都至少吃掉 $b$ 份。

为了尽可能保护自己的味觉,诚希望最大化自己从这顿午餐中获得的总美味值。

形式化地,设诚最终选择吃掉的料理集合为 $S$,所有相互作用关系构成的集合为 $E$,则总美味值定义为 $$\sum_{i\in S} w_i+\sum_{\substack{(u,v,W)\in E\\u\in S,\,v\in S}} W.$$

你需要求出满足条件的集合 $S$ 的最大总美味值。

输入描述:

第一行包含一个整数 $T \left(1 \leq T \leq 100\right)$,表示测试用例的组数。

对于每组测试用例,第一行包含四个整数 $n, m, k, b \left(1 \leq n \leq 200, 0 \leq m < n, 1 \leq k \leq n, 0 \leq b \leq \lfloor \frac{k}{2} \rfloor\right)$,分别表示料理的数量、相互作用关系的数量、至少需要吃掉的料理数量、每种属性至少需要吃掉的料理数量。

第二行包含 $n$ 个整数 $w_1, w_2, \cdots, w_n \left(-10^9 \leq w_i \leq 0\right)$,表示每种料理的美味值。

第三行包含 $n$ 个整数 $c_1, c_2, \cdots, c_n \left(c_i \in \{0,1\}, b \leq \sum c_i \leq n - b\right)$,表示每种料理的属性。

接下来 $m$ 行,每行包含三个整数 $u_i, v_i, W_i \left(1 \leq u_i, v_i \leq n, u_i \neq v_i, -10^9 \leq W_i \leq 0\right)$,表示第 $u_i$ 种料理与第 $v_i$ 种料理之间存在一组相互作用。

保证由这 $m$ 组关系构成的无向图是一片森林,即每个连通块都是一棵树。保证不存在两组关系连接同一对料理。

保证所有测试用例中 $n$ 的总和不超过 $200$,且所有测试用例中 $k$ 的总和不超过 $200$。

输出描述:

对于每组测试用例,输出一个整数,表示诚能够获得的最大总美味值。

示例1

输入

3
4 1 2 1
-1 -2 -3 -4
0 0 1 1
1 3 -5
5 3 3 1
-3 -2 -4 -1 -5
0 1 0 1 0
1 2 -2
2 3 -3
4 5 -1
6 3 5 2
-10 -8 -5 -1 -2 -4
0 0 1 1 1 1
1 2 -9
3 4 -5
5 6 -10

输出

-5
-8
-40

说明

对于第一组测试用例,在满足至少选择 $2$ 种料理,且两种属性都至少选择 $1$ 种料理的前提下,最优方案为选择第 $1, 4$ 种料理或第 $2, 3$ 种料理,总美味值均为 $−5$。

 

解题思路

  由于每个节点可以选择或不选,且要求最大值,所以大概率是树上背包问题。然而,题目给定的是由若干棵树构成的森林,若对每棵树单独处理再合并结果会较为繁琐。因此我们可以引入一个虚拟节点(不妨设为 $0$ 号节点),并将其与森林中每棵树的根节点相连,从而将森林转化为一棵以 $0$ 为根的树。由于虚拟节点本身不提供实际贡献,最终只需在所有不选虚拟节点的方案中寻找最大值即可。

  定义状态 $f(u,c,i,j,k)$ 表示在以 $u$ 为根的子树中,考虑其前 $c$ 个子节点的子树,恰好选择了 $i$ 个属性为 $0$ 的节点和 $j$ 个属性为 $1$ 的节点,且节点 $u$ 自身的状态为 $k$($k=0$ 表示不选,$k=1$ 表示选)时的最大值。由于状态中已经分别记录了两种属性的节点选择数量,两者的和 $i+j$ 即为总选择节点数,因此无需在状态中额外增加维度来记录所选的总点数。转移时,我们枚举第 $c$ 个子节点的子树状态,将其与前 $c-1$ 个子节点的子树状态进行合并。容易得到一般的状态转移方程为 $$f(u,c,i,j,k) = \max_{0 \leq x \leq n}{\max_{0\leq y \leq n}\left\{f(u,c-1,i-x,j-y,k) + \max_{z \in \{0,1\}}{\left\{f(v_c,m_{v_c},x,y,z)+[k \wedge z] \cdot w_{u,v_c}\right\}}  \right\}} \quad \left(0 \leq i \leq n, 0 \leq j \leq n, k \in \{0,1\} \right)$$

  其中 $v_c$ 表示 $u$ 的第 $c$ 个子节点,$m_u$ 表示以 $u$ 为根的子树大小,$w_{u,v}$ 表示边 $(u,v)$ 的权值。上述朴素转移枚举了大量无效状态,导致整个 dp 的复杂度为 $O\left(n^5\right)$,对此我们需要缩小枚举边界来降低复杂度。

  假设在 $u$ 的前 $c$ 个子节点子树中,属性为 $0$ 与 $1$ 的节点总数分别为 $s_{u,c,0}$ 和 $s_{u,c,1}$,那么状态 $i$ 与 $j$ 更紧致的合法区间为 $\cases{0 \leq i \leq s_{u,c,0} \\ 0 \leq j \leq s_{u,c,1}}$。同理,在以 $v_c$ 为根的子树中,选择属性为 $0$ 与 $1$ 的数量 $x$ 和 $y$ 也需要满足 $\cases{0 \leq x \leq s_{v_c,m_{v_c},0} \\ 0 \leq y \leq s_{v_c,m_{v_c},1}}$。同时,$x$ 和 $y$ 的取值还必须保证前 $c-1$ 个子节点的子树状态合法,即需要满足 $\cases{x \leq i \\ i - x \leq s_{u,c-1,0}}$,以及 $\cases{y \leq j \\ j - y \leq s_{u,c-1,1}}$。将这些约束条件综合起来,便能得到 $x$ 和 $y$ 的取值范围 $\cases{\max\{0, i - s_{u,c-1,0}\} \leq x \leq \min\{i, s_{v_c,m_{v_c},0}\} \\ \max\{0, j - s_{u,c-1,1}\} \leq y \leq \min\{j, s_{v_c,m_{v_c},1}\}}$。

  于是,优化后的转移方程为 $$f(u,c,i,j,k) = \max_{\substack{\max\{0, i - s_{u,c-1,0}\} \leq x \leq \min\{i, s_{v_c,m_{v_c},0}\} \\ \max\{0, j - s_{u,c-1,1}\} \leq y \leq \min\{j, s_{v_c,m_{v_c},1}\}}}{\left\{f(u,c-1,i-x,j-y,k) + \max_{z \in \{0,1\}}{\left\{f(v_c,m_{v_c},x,y,z)+[k \wedge z] \cdot w_{u,v_c}\right\}}  \right\}} \quad \left(0 \leq i \leq s_{u,c,0}, 0 \leq j \leq s_{u,c,1}, k \in \{0,1\} \right)$$

  可以证明,经过边界优化后的整体复杂度降为 $O\left(n^4\right)$,其复杂度证明可以参考博客 [CTSC1997] 选课 中的相关分析。

  最终统计答案时,由于设定了 $0$ 号虚拟节点且必须不选,同时题目要求选出的节点总数至少为 $k$,且两种属性均至少选择 $b$ 个,因此最终答案为 $\max\limits_{\substack{b \leq i \leq n \\ \max\{b,k-i\} \leq j \leq n}}{\left\{f(0,m_{0},i,j,0)\right\}}$。在代码实现中,通过倒序枚举 $i$ 和 $j$ 的方式,省略了状态中关于前 $c$ 个子节点的这一维度。

  AC 代码如下,时间复杂度为 $O\left(n^4\right)$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 205, M = N * 2;

int n, m, k, b;
int a[N], c[N];
int h[N], e[M], wt[M], ne[M], idx;
int fa[N], s[N][2];
LL f[N][N][N][2];

void add(int u, int v, int w) {
    e[idx] = v, wt[idx] = w, ne[idx] = h[u], h[u] = idx++;
}

int find(int x) {
    return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}

void dfs(int u, int p) {
    f[u][0][0][0] = 0;
    if (c[u]) {
        f[u][0][1][1] = a[u];
        s[u][0] = 0, s[u][1] = 1;
    }
    else {
        f[u][1][0][1] = a[u];
        s[u][0] = 1, s[u][1] = 0;
    }
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i], w = wt[i];
        if (v == p) continue;
        dfs(v, u);
        s[u][0] += s[v][0];
        s[u][1] += s[v][1];
        for (int i = s[u][0]; i >= 0; i--) {
            for (int j = s[u][1]; j >= 0; j--) {
                for (int k = 0; k <= 1; k++) {
                    for (int x = max(0, i - s[u][0] + s[v][0]); x <= min(i, s[v][0]); x++) {
                        for (int y = max(0, j - s[u][1] + s[v][1]); y <= min(j, s[v][1]); y++) {
                            f[u][i][j][k] = max(f[u][i][j][k], f[u][i - x][j - y][k] + max(f[v][x][y][0], f[v][x][y][1] + k * w));
                        }
                    }
                }
            }
        }
    }
}

void solve() {
    cin >> n >> m >> k >> b;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
    }
    idx = 0;
    memset(h, -1, n + 1 << 2);
    iota(fa + 1, fa + n + 1, 1);
    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w);
        add(v, u, w);
        fa[find(u)] = find(v);
    }
    for (int i = 1; i <= n; i++) {
        if (fa[i] == i) add(0, i, 0);
    }
    memset(f, 0xc0, sizeof(f));
    dfs(0, -1);
    LL ret = 0xc0c0c0c0c0c0c0c0;
    for (int i = b; i <= n; i++) {
        for (int j = max(b, k - i); j <= n; j++) {
            ret = max(ret, f[0][i][j][0]);
        }
    }
    cout << ret << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  2026年中南民族大学程序设计竞赛决赛 题解:https://uploadfiles.nowcoder.com/files/20260525/464442_1779705314572/2026%E5%B9%B4%E4%B8%AD%E5%8D%97%E6%B0%91%E6%97%8F%E5%A4%A7%E5%AD%A6%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E7%AB%9E%E8%B5%9B%E5%86%B3%E8%B5%9B%E8%B5%9B%E9%A2%98%E8%AE%B2%E8%A7%A3.pdf

posted @ 2026-05-30 22:06  onlyblues  阅读(15)  评论(0)    收藏  举报
Web Analytics