D-牛牛种小树_牛客练习赛89(完全背包)

D-牛牛种小树_牛客练习赛89 (nowcoder.com)

题目描述

牛牛最近在学数据结构。他打算用他得到的米粒去构造一棵有n个节点的树, 并使得它的价值最大。 设f(d)表示树上度数为d的一个点能够获取的最大价值。则这棵树的价值为 ∑ i = 1 n f ( d i ) \sum_{i=1}^{n} f\left(d_{i}\right) i=1nf(di), 其中 d i d_{i} di 表示第i个点的度数。

数据范围

1 < n ⩽ 1 e 4 , 1 ⩽ f ( i ) ⩽ 1 e 9 1<n⩽1e4 , 1⩽f(i)⩽1e9 1<n1e4,1f(i)1e9

思路

首先我们可以将所有点看成连有一条边的孤立的点,其度为1.我们又知道 一棵树的度之和为 2 ∗ n − 2 2 *n - 2 2n2 所以还未分配的度数为 n − 2 n - 2 n2

那么接下来对度数进行完全背包,背包的容量即为还未分配的度数 n − 2 n - 2 n2

第一重循环枚举度数 i ,第二重枚举背包容量,第三重循环枚举能将多少个节点置成当前的度数 i i i

因为点数为 n 的树中,一个节点的最大度数为 n − 1 n - 1 n1,且每个点已经分配了一个度, 所以 i i i 2 2 2 枚举到 n − 1 n-1 n1

朴素的dp过程如下:

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示已经求出了将若干度为 1 1 1 的点的度数置成 2 2 2 ~ i − 1 i - 1 i1 中的一个且消耗了 j j j 个自由的度得到的最大值。

for(int i = 2; i < n;++i){ // 度数
    for(int j = 0;j <= n - 2;++j){ // 背包容量
        for(int k = 0; k * i <= j;++k){ // 将 k 个点的度数置为 i
            // 之所以 + f[i] - f[1] 是因为将当前点的度数变为 i 可以得到 f[i]的贡献
            // 但是之前这个点的度数为 1,所以要减去 f[1]
            dp[i][j] = max(dp[i][j],dp[i - 1][j - (k * i)] + f[i] - f[1]);
        }
    }
}

答案为 d p [ n − 1 ] [ n − 2 ] dp[n - 1][n - 2] dp[n1][n2]

根据完全背包的优化原理,可以得到下面的代码:

代码

#include<bits/stdc++.h>
#include<unordered_map>
#define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x) cerr << " == " << (x) << endl;
using namespace std;

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
//template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }

const int N = 1e5 + 10;
int n;
int dp[N];
int f[N];

void solve() {
	cin >> n;
	for (int i = 1; i < n; ++i) {
		cin >> f[i];
	}

	dp[0] = 1ll * n * f[1];

	for (int i = 2; i <= n; ++i) {
		for (int j = i - 1; j <= n - 2; ++j) {
			dp[j] = max(dp[j], dp[j - (i - 1)] + f[i] - f[1]);
		}
	}

	cout << dp[n - 2ll] << endl;
}

signed main() {
	// int _; cin >> _;
	// while (_--)
		solve();

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzqwtc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值