[CF815C] Karen and Supermarket

Karen and Supermarket

题意:
n n n个商品,第i个商品的价格为 c i c_i ci美元,优惠劵 d i d_i di美元,对于 i ≥ 2 i \ge 2 i2,使用优惠劵的前提是必须先使用 x i x_i xi的优惠劵。现问你 b b b美元最多能买多少个商品?

分析:
很明显,是一道有依赖的背包问题,树形背包。这也是我做的第一道树形背包。
我们设 d p [ u ] [ c n t ] [ 0 / 1 ] dp[u][cnt][0/1] dp[u][cnt][0/1]表示以 u u u为根的子树中, u u u用或不用优惠劵选了 c n t cnt cnt个商品的最小费用。
仔细想想能得出状态转移方程如下:

  • d p [ u ] [ i + j ] [ 0 ] = m i n ( d p [ u ] [ i + j ] [ 0 ] , d p [ u ] [ i ] [ 0 ] + d p [ v ] [ j ] [ 0 ] ) dp[u][i + j][0] = min(dp[u][i + j][0], dp[u][i][0] + dp[v][j][0]) dp[u][i+j][0]=min(dp[u][i+j][0],dp[u][i][0]+dp[v][j][0])
  • d p [ u ] [ i + j ] [ 0 ] = m i n ( d p [ u ] [ i + j ] [ 0 ] , d p [ u ] [ i ] [ 1 ] + m i n ( d p [ v ] [ j ] [ 0 ] , d p [ v ] [ j ] [ 1 ] ) ) dp[u][i + j][0] = min(dp[u][i + j][0], dp[u][i][1] + min(dp[v][j][0], dp[v][j][1])) dp[u][i+j][0]=min(dp[u][i+j][0],dp[u][i][1]+min(dp[v][j][0],dp[v][j][1]))

我们 D F S DFS DFS的同时进行状态转移,最后我们只需要从 n n n往前枚举判断第一个符合 d p [ 1 ] [ i ] [ 0 ] < = b ∣ ∣ d p [ 1 ] [ i ] [ 1 ] < = b dp[1][i][0] <= b || dp[1][i][1] <= b dp[1][i][0]<=bdp[1][i][1]<=b的就是我们最多能买的商品数了。

#include <bits/stdc++.h>

using namespace std;
const int maxn = 5e3 + 5;

struct Node {
    int c, d, w;
    Node(int c = 0, int d = 0, int w = 0):c(c),d(d),w(w){}
} a[maxn];

struct Edge {
    int to, next;
    Edge(int to = 0, int next = 0):to(to),next(next){}
} edges[maxn];

int tot = 1, head[maxn];

void addEdge(int u, int v) {
    edges[++tot] = {v, head[u]};
    head[u] = tot;
}

int dp[maxn][maxn][2]; // dp[i][j][0/1]表示以i为根的子树选j个物品,u用或不用优惠卷的最小代价
int son[maxn];

void dfs(int u) {
    son[u] = 1;
    dp[u][0][0] = 0;
    dp[u][1][0] = a[u].c;
    dp[u][1][1] = a[u].w;
    for (int i = head[u], v; i; i = edges[i].next) {
        v = edges[i].to;
        dfs(v);
        for (int j = son[u]; j >= 0; j--) {
            for (int k = 0; k <= son[v]; k++) {
                dp[u][j + k][0] = min(dp[u][j + k][0], dp[u][j][0] + dp[v][k][0]);
                dp[u][j + k][1] = min(dp[u][j + k][1], dp[u][j][1] + min(dp[v][k][1], dp[v][k][0]));
            }
        }
        son[u] += son[v];
    }
}

int main() {
    int n, b;
    memset(dp, 0x3f, sizeof(dp));
    scanf("%d%d", &n, &b);
    for (int i = 1, j; i <= n; i++) {
        scanf("%d%d", &a[i].c, &a[i].d);
        a[i].w = a[i].c - a[i].d;
        if (i > 1) {
            scanf("%d", &j);
            addEdge(j, i);
        }
    }
    dfs(1);
    for (int i = n; i >= 0; i--) if (dp[1][i][1] <= b || dp[1][i][0] <= b) return printf("%d\n", i), 0;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值