「刷题记录」LOJ/一本通提高篇 树型动态规划

「二叉苹果树」#

题目传送门:二叉苹果树
状态:
dp(i,j) : 以 i 为根节点的子树选 j 条枝条的最大价值
dp(i,j)=max(dp(i,j),dp(i,jk1)+dp(soni,k)+w)
树上背包

点击查看代码
/*
  date: 2022.9.2
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 300;
int n, m, cnt;
int h[N], dp[N][N], sz[N];

struct edge {
	int u, v, nxt;
	ll w;
} e[N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v, ll w) {
	e[++cnt].u = u;
	e[cnt].v = v;
	e[cnt].w = w;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u, int fat) {
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v, w = e[i].w;
		if(v == fat)
			continue;
		dfs(v, u);
		sz[u] += (sz[v] + 1);
		for (int j = min(m, sz[u]); j; --j) {
			for (int k = min(j - 1, sz[v]); k >= 0; --k) {
				dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[v][k] + w);
			}
		}
	}
}

int main() {
	n = read(), m = read();
	for (int u, v, i = 1; i < n; ++i) {
		u = read(), v = read();
		ll w = read();
		add(u, v, w);
		add(v, u, w);
	}
	dfs(1, 0);
	printf("%d\n", dp[1][m]);
	return 0;
}

「选课」#

题目传送门:选课
状态:
dp(i,j):在以 i 为根的子树中选 j 门课
dp(i,j)=max(dp(i,j),dp(soni,k)+dp(i,jk))
树上背包

点击查看代码
/*
  date: 2022.9.2
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 310;
int n, m, cnt;
int h[N], dp[N][N], siz[N];

struct edge {
	int v, nxt, w;
} e[N << 1];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u) {
	siz[u] = 1;
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		dfs(v);
		siz[u] += siz[v];
		for (int j = min(n, siz[u]); j >= 1; --j) {
			for (int k = min(j - 1, siz[v]); k >= 0; --k) {
				dp[u][j] = max(dp[u][j], dp[v][k] + dp[u][j - k]);
			}
		}
	}
}

int main() {
	m = read(), n = read();
	for (int v, i = 1; i <= m; ++i) {
		v = read(), dp[i][1] = read();
		add(v, i);
	}
	++n;
	dfs(0);
	printf("%d\n", dp[0][n]);
	return 0;
}

「数字转换」#

题目传送门:数字转换
求树的直径,预处理出能相互转换的数字

点击查看代码
/*
  date: 2022.9.2
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 5e4 + 5;
int n, cnt, k, maxn;
int sum[N], h[N];

struct edge {
	int v, nxt;
} e[N << 1];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void init() {
	for (int i = 1; i <= n; ++i) {
		if (sum[i] <= i && sum[i] != 0) {
			add(i, sum[i]);
			add(sum[i], i);
		}
		for (int j = (i << 1); j <= n; j += i) {
			sum[j] += i;
		}
	}
}

void dfs(int u, int fat, int len) {
	if (len > maxn) {
		k = u;
		maxn = len;
	}
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fat)
			continue;
		dfs(v, u, len + 1);
	}
}

int main() {
	n = read();
	init();
	dfs(1, 0, 0);
	maxn = 0;
	dfs(k, 0, 0);
	printf("%d\n", maxn);
	return 0;
}

「战略游戏」#

题目传送门:战略游戏
状态:
dp(i,0/1):第 i 个位置放不放士兵,0 代表不放,1 代表放
dp(i,0)+=dp(soni,1)
dp(i,1)+=max(dp(soni,0),dp(soni,1))

点击查看代码
/*
  date: 2022.9.2
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int N = 1505;
int n, cnt;
int h[N], dp[N][2], son[N];

struct edge {
	int v, nxt;
} e[N << 1];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u, int fat) {
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fat)
			continue;
		dfs(v, u);
		dp[u][0] += dp[v][1];
		dp[u][1] += min(dp[v][0], dp[v][1]);
	}
	++dp[u][1];
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		int u = read() + 1;
		son[u] = read();
		for (int j = 1; j <= son[u]; ++j) {
			int v = read() + 1;
			add(u, v);
			add(v, u);
		}
	}
	dfs(1, 0);
	printf("%d\n", min(dp[1][0], dp[1][1]));
	return 0;
}

「皇宫看守」#

题目传送门:皇宫看守
状态:
dp(i,0/1/2): 在 i 位置上以及周围位置上是否有士兵
0 表示 i 位置上没有士兵,且父亲也没放
1 表示 i 位置上有士兵
2 表示 i 的父亲放了士兵
dp(i,1)+=min(min(dp(v,0),dp(v,1)),dp(v,2))
dp(i,2)+=min(dp(v,0),dp(v,1))
如果 i 位置没放士兵,则他至少要有一个孩子放置士兵,最初的 dp
dp(i,0)+=min(dp(v,0),dp(v,1))
然后再扫一遍孩子
如果有 dp(i,1)<dp(i,0) 的情况,直接退出即可
否则,按照转移方程就是孩子们没有一个防止士兵,在扫的过程中,我们记录一下差 dp(v,1)dp(v,0),将这些差取一个最小值 min,最后在加入到 dp(i,0)

点击查看代码
/*
  date: 2022.9.2
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 1510;
const int inf = ~(1 << 31);
int n, cnt;
int w[N], h[N], dp[N][3];

struct edge {
	int v, nxt;
} e[N << 1];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u, int fat) {
	int fg = 0, cha = inf;
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if(v == fat)
			continue;
		dfs(v, u);
		dp[u][1] += min(min(dp[v][1], dp[v][0]), dp[v][2]);
		dp[u][0] += min(dp[v][1], dp[v][0]);
		dp[u][2] += min(dp[v][1], dp[v][0]);
	}
	dp[u][1] += w[u];
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if(v == fat)
			continue;
		if(dp[v][1] < dp[v][0]) {
			fg = 1;
			break;
		}
		else {
			cha = min(cha, dp[v][1] - dp[v][0]);
		}
	}
	if(!fg) {
		dp[u][0] += cha;
	}
}

int main() {
	n = read();
	for (int i = 1, u, k; i <= n; ++i) {
		u = read();
		w[u] = read();
		k = read();
		for (int j = 1, v; j <= k; ++j) {
			v = read();
			add(u, v);
			add(v, u);
		}
	}
	dfs(1, 0);
	printf("%d\n", min(dp[1][0], dp[1][1]));
	return 0;
}

「加分二叉树」#

题目传送门:加分二叉树
状态:
dp(i,j): (ij) 的最大得分
dp(i,j)=max(dp(i,j),dp(i,k1)+dp(k,k)+dp(k+1,j))

点击查看代码
/*
  date: 2022.9.2
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 50;
int n;
int dp[N][N], root[N][N];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void print(int l, int r) {
	if (l > r)
		return ;
	int k = root[l][r];
	printf("%d ", k);
	if (l == r)
		return ;
	print(l, k - 1);
	print(k + 1, r);
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		dp[i][i] = read();
		root[i][i] = i;
		dp[i][i - 1] = 1;
	}
	for (int len = 1; len < n; ++len) {
		for (int l = 1; l + len <= n; ++l) {
			int r = l + len;
			dp[l][r] = dp[l + 1][r] + dp[l][l];
			root[l][r] = root[l][l];
			for (int k = l + 1; k < r; ++k) {
				if(dp[l][r] < dp[l][k - 1] * dp[k + 1][r] + dp[k][k]) {
					dp[l][r] = dp[l][k - 1] * dp[k + 1][r] + dp[k][k];
					root[l][r] = k;
				}
			}
		}
	}
	printf("%d\n", dp[1][n]);
	print(1, n);
	return 0;
}

「旅游规划」#

题目传送门:旅游规划
求树的直径,具体看代码

点击查看代码
/*
  date: 2022.9.2
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 2e5 + 5;
int n, cnt, maxn, k;
int h[N], d1[N], d2[N], pos[N], up[N];

struct edge {
	int v, nxt;
} e[N << 1];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u, int fat, int len) {
	if (len > maxn) {
		maxn = len;
		k = u;
	}
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fat)
			continue;
		dfs(v, u, len + 1);
	}
}

void tredp_down(int u, int fat) {
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fat)
			continue;
		tredp_down(v, u);
		if (d1[v] + 1 > d1[u]) {
			d2[u] = d1[u];
			d1[u] = d1[v] + 1;
			pos[u] = v;
		}
		else {
			if (d1[v] + 1 > d2[u]) {
				d2[u] = d1[v] + 1;
			}
		}
	}
}

void tredp_up(int u, int fat) {
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fat)
			continue;
		up[v] = up[u] + 1;
		if (v == pos[u]) {
			up[v] = max(up[v], d2[u] + 1);
		}
		else {
			up[v] = max(up[v], d1[u] + 1);
		}
		tredp_up(v, u);
	}
}

int main() {
	n = read();
	for (int i = 1, u, v; i < n; ++i) {
		u = read() + 1, v = read() + 1;
		add(u, v);
		add(v, u);
	}
	dfs(1, 0, 0);
	maxn = 0;
	dfs(k, 0, 0);
	tredp_down(1, 0);
	tredp_up(1, 0);
	for (int i = 1; i <= n; ++i) {
		if (d1[i] + d2[i] == maxn || d1[i] + up[i] == maxn)
			printf("%d\n", i - 1);
	}
	return 0;
}

「周年纪念晚会」#

题目传送门:周年纪念晚会
状态:
dp(i,0/1): 第 i 个人去不去,0 表示不去,1 表示去
dp(i,0)+=max(dp(v,0),dp(v,1))
dp(i,1)+=dp(v,0)

点击查看代码
/*
  date: 2022.9.3
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 6010;
int n, cnt;
int happy[N], h[N], dp[N][2];

struct edge {
	int v, nxt;
} e[N << 1];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u, int fat) {
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fat)
			continue;
		dfs(v, u);
		dp[u][1] += dp[v][0];
		dp[u][0] += max(dp[v][1], dp[v][0]);
	}
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		dp[i][1] = read();
	}
	int l, k;
	while ((l = read()) && (k = read())) {
		add(l, k);
		add(k, l);
	}
	dfs(1, 0);
	printf("%d\n", max(dp[1][1], dp[1][0]));
	return 0;
}

「叶子的染色」#

题目传送门:叶子的染色
状态:
dp(i,0/1):第 i 片叶子被染色,0 代表白色,1代表黑色
dp(i,0)+=max(dp(v,0)1,dp(v,1))
dp(i,1)+=max(dp(v,1)1,dp(v,0))

点击查看代码
/*
  date: 2022.9.3
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 1e4 + 5;
const int inf = ~(1 << 31);
int n, m, cnt, rt;
int col[N], h[N], dp[N][2];

struct edge {
	int v, nxt;
} e[N << 1];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u, int fat) {
	if (u <= n)
		return ;
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fat)
			continue;
		dfs(v, u);
		dp[u][0] += min(dp[v][0] - 1, dp[v][1]);
		dp[u][1] += min(dp[v][1] - 1, dp[v][0]);
	}
}

int main() {
	m = read();
	n = read();
	rt = n + 1;
	for (int i = 1; i <= n; ++i) {
		col[i] = read();
	}
	for (int i = 1, x, y; i < m; ++i) {
		x = read();
		y = read();
		add(x, y);
		add(y, x);
	}
	for (int i = 1; i <= m; ++i) {
		dp[i][1] = dp[i][0] = 1;
		if (i <= n) {
			dp[i][!col[i]] = inf;
		}
	}
	dfs(rt, 0);
	printf("%d\n", min(dp[rt][1], dp[rt][0]));
	return 0;
}

「骑士」#

题目传送门:骑士
状态:
dp(i,0/1):第 i 个骑士选不选,0 代表不选,1 代表选
dalao 的题解:题解

点击查看代码
/*
  date: 2022.9.3
  worked by yi_fan0305
 */
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

const int N = 1e6 + 5;
int n, cnt;
ll ans;
int vi[N], fa[N], h[N], vis[N];
ll val[N], dp[N][2];// 1 选 0 不选

struct edge {
	int v, nxt;
} e[N << 1];

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? ~x + 1 : x;
}

void add(int u, int v) {
	e[++cnt].v = v;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}

void dfs(int u) {
	vi[u] = 1;// 避免重判
	dp[u][1] = val[u];
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (vi[v]) 
			continue;
		dfs(v);
		dp[u][0] += max(dp[v][0], dp[v][1]);
		dp[u][1] += dp[v][0];
	}
}

void find(int u) {
	int rt;
	for (rt = u; !vis[rt]; rt = fa[rt]) {
		vis[rt] = 1;
	}
	dfs(rt);
	u = fa[rt];
	dp[u][1] = dp[u][0];// 强制不选 u 点
	for (u = fa[u]; u != rt; u = fa[u]) {// 不选 u 点,更新一遍
		dp[u][1] = val[u];
		dp[u][0] = 0;
		for (int i = h[u]; i; i = e[i].nxt) {
			int v = e[i].v;
			dp[u][1] += dp[v][0];
			dp[u][0] += max(dp[v][1], dp[v][0]);
		}
	}
	dp[rt][1] = val[u];
	for (int i = h[rt]; i; i = e[i].nxt) {// 重搜一遍
		int v = e[i].v;
		dp[rt][1] += dp[v][0];
	}
	ans += max(dp[rt][0], dp[rt][1]);
}

int main() {
	n = read();
	for (int i = 1; i <= n; ++i) {
		val[i] = read();
		fa[i] = read();
		add(fa[i], i);
	}
	for (int i = 1; i <= n; ++i) {
		if (!vi[i]) {
			find(i);
		}
	}
	printf("%lld\n", ans);
	return 0;
}

作者:yifan0305

出处:https://www.cnblogs.com/yifan0305/p/16652188.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载时还请标明出处哟!

posted @   yi_fan0305  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示