CCPC-Wannafly Winter Camp Day1 (Div2, onsite) E 流流流动 树形dp

题解

按照题意将节点之间连线后 每个联通块无环但是整体不一定联通 使用并查集将每个集合都跟0号节点连一根线 建立以0为根的树
使用树形dp d[i][0/1]表示以i为根的子树i这个节点1选/0不选的最大收益 DFS从0遍历整棵树回溯后计算d
如果不选当前节点x则直接加上子节点y选或者不选 即d[x][0] += max(d[y][0], d[y][1])
如果选当前节点x从子节点y转移时候 如果选y择需要减去一部分收益 即d[x][1] += max(d[y][0], d[y][1] - g[min(x, y)])

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 1e2 + 10;
int vex[MAXN];
int f[MAXN], g[MAXN];
int d[MAXN][2]; //以i为根的子树1选/0不选的最大收益
vector<int> e[MAXN];

inline int findr(int x)
{
	return vex[x] == 0 ? x : vex[x] = findr(vex[x]);
}
inline void join(int x, int y)
{
	vex[findr(y)] = findr(x);
}
void DFS(int x, int fz)
{
	d[x][1] = f[x]; //选x
	for (int y : e[x]) if (y != fz)
	{
		DFS(y, x);
		d[x][0] += max(d[y][0], d[y][1]); //加上子节点选或不选
		d[x][1] += max(d[y][0], d[y][1] - g[min(x, y)]); //都选损失g[min(x, y)]
	}
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		scanf("%d", &f[i]);
	for (int i = 1; i <= n; i++)
		scanf("%d", &g[i]);
	for (int i = 2; i <= n; i++)
		if ((i & 1) == 0) //奇偶建边
			e[i].push_back(i / 2), e[i / 2].push_back(i), join(i, i / 2);
		else if (i * 3 + 1 <= n)
			e[i].push_back(i * 3 + 1), e[i * 3 + 1].push_back(i), join(i, i * 3 + 1);
	for (int i = 1; i <= n; i++) 
		if (!vex[i]) //最后将每个集合和0号节点建立一个连线
			e[0].push_back(i), e[i].push_back(0);
	DFS(0, -1);
	cout << d[0][0] << endl; //输出不选根

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值