最近比赛中遇到的几道dp题

1、2015 icpc 长春-H-Partial Tree(据说是完全背包,但我觉得不像)

一、题意

给定$n$个点,每一个点$i$的权值为关于度数$d_i$的函数$f(d_i),$让你构建一棵树,使得这棵树的总度数和最大。

二、思路
前提结论:任意一个累加和为$2*(n-1)$的不含$0$元素的序列,将其表示成点的度数,都一定可以组成一棵树。
一棵$n-1$条边的树,有$2*(n-1)$个度。先给每个点分一个度,还剩$n-2$个度。剩下来就是整数划分了。$g[i]$表示把数字$i$划分成若干部分所能得到的最大度函数和。那么,容易得到递推式:$g[i]=max\{g[k]+f(i-k+1)-f(1)|0 \le k < i\}$。初始化$g$数组为$-\infty$,初始状态$g[0]=0$。最终答案就是$g[n-2]+n*f(1)$。
解释一下递推式的含义:把$i$个糖果分成若干份所能得到的最大度函数和,等于把$k$个糖果分成若干份,然后剩余的$i-k$份变成一份。要注意的是,转移的代价不是$f(i-k)$,而是$f(i-k+1)$,意思是:当给某个点(我也不知道前面已经分成几份了)$i-k$个“糖果”时,它实际上有的糖果是$i-k+1$个,因为一开始给它分了$1$个。但是又不能只加$f(i-k+1)$,因为还要减去最后加的$n*f(1)$份中的$1*f(1)$份。换句话说,为什么要减这部分,主要是我们并不清楚$n-2$个糖果到底分成了几份,如果转移的时候只加$f(i-k+1)$,那最后不知道要加多少倍$f(1)$,所以,这不好弄。就只能在转移的时候减了。

三、代码

#include<bits/stdc++.h>
using namespace std;
int n, f[2018], g[2018];
int main() {
//    freopen("h.in","r",stdin);
    int T;
    for(scanf("%d", &T); T--;) {
        scanf("%d", &n);
        for(int i = 1; i < n; ++i)scanf("%d", f + i);
        memset(g, 0xcf, sizeof(g[0])*n);
        g[0] = 0;
        for(int i = 1; i <= n - 2; ++i) {
            for(int j = 0; j <= i; ++j) {
                g[i] = max(g[i], g[j] + f[i - j + 1] - f[1]);
            }
        }
        printf("%d\n", g[n - 2] + n * f[1]);
    }
    return 0;
}

 

2、2017 ccpc final-G-Alice’s Stamps(像个01背包)

3、2018 icpc 焦作网络赛-运输船(裸的多重背包)

https://www.cnblogs.com/565261641-fzh/p/9652143.html

4、安装App(01背包,记录路径)

https://www.cnblogs.com/565261641-fzh/p/9651977.html

转载于:https://www.cnblogs.com/fuzhihong0917/p/9711325.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值