树形DP各类题目的考试总结

Bribing FIPA

第一眼,好题!
第二眼,**输入。

合着题目是黄,输入是黑。

我被骗了。

做法

输入

有一个好用的东西是stringstream

这玩意自己去了解,挺好用的。

DP

就是模版背包

代码

戳我
#include <bits/stdc++.h>
#define IAKIOI ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

const int N = 3000 + 10, INF = 1e9;

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

int n, m;
vector<int> g[N];
int dp[N][N];
int val[N], sz[N], d[N];

void dfs(int u) {
	sz[u] = 1;
	for (int i = 1; i <= n; i++) dp[u][i] = INF;
	dp[u][0] = 0;
	for (auto v : g[u]) {
		dfs(v);
		for (int j = sz[u]; j >= 0; j--) {
			if (dp[u][j] >= INF) continue;
			for (int k = sz[v]; k >= 0; k--) {
				if (dp[v][k] >= INF) continue;
				dp[u][j + k] = min(dp[u][j + k], dp[u][j] + dp[v][k]);
			}
		}
		sz[u] += sz[v];
	}
	if (u != 0) dp[u][sz[u]] = min(dp[u][sz[u]], val[u]);
}

int main() {
//		freopen("fipa.in", "r", stdin);
//		freopen("fipa.out", "w", stdout);
	IAKIOI
	string L;
	while (1) {
		if (!getline(cin, L) || L == "#") break;
		stringstream S(L);
		S >> n >> m;
		for (int i = 0; i <= n; i++) g[i].clear(), d[i] = 0;
		unordered_map<string, int> mp;
		int cnt = 1;
		vector<pair<int, vector<string> > > a;
		for (int i = 1; i <= n; i++) {
			getline(cin, L);
			stringstream s(L);
			string name;
			int w;
			s >> name >> w;
			if (!mp.count(name)) mp[name] = cnt++;
			int id = mp[name]; val[id] = w;
			vector<string> c;
			string Name;
			while (s >> Name) c.push_back(Name);
			a.push_back({id, c});
		}
		for (auto X : a) {
			int u = X.first;
			for (auto name : X.second) {
				int v = mp[name];
				if (!mp.count(name)) v = mp[name] = cnt++;
				g[u].push_back(v), d[v]++;
			}
		}
		for (int i = 1; i <= n; i++) if (!d[i]) g[0].push_back(i);
		dfs(0);
		int ans = INF;
		for (int i = m; i <= n; i++) ans = min(ans, dp[0][i]);
		cout << ans << '\n';
	}
	return 0;
}

时间分配与预期

花了an hour,主要用在了输入上,想了好久才想起来。

预期:100pts,实际:Wendaysif的std挂了,其实就是100pts

[HAOI2009] 毛毛虫

其实就是直径的类似。

做法

DP求最大值和次大值,但是是维护的度数的最大和次大。

最后的答案要加上头和尾。

代码

点击查看代码
#include <bits/stdc++.h>
#define IAKIOI ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

const int N = 3e5 + 10, INF = 1e9;

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

vector<int> g[N];
int d[N], dp[N];
int ans;

void dfs(int u, int fa) {
	dp[u] = d[u] - 1;
	int Max1 = 0, Max2;
	for (auto v : g[u]) {
		if (v == fa) continue;
		dfs(v, u);
		if (Max1 < dp[v]) Max2 = Max1, Max1 = dp[v];
		else if (Max2 < dp[v]) Max2 = dp[v];
	}
	ans = max(ans, Max1 + Max2 + d[u] - 1), dp[u] += Max1;
}

int main() {
	//freopen("worm.in", "r", stdin);
	//freopen("worm.out", "w", stdout);
	IAKIOI
	int n, m;
	cin >> n >> m;
	if (n == 1) {
		cout << 1;
		return 0;
	}
	for (int i = 1; i <= m; i++) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v), g[v].push_back(u);
		d[u]++, d[v]++;
	}
	dfs(1, 0);
	cout << ans + 2;
	return 0;
}

时间分配与预期

用了1hour,主要是愣了好久没反应过来

预期:100pts
实际:100pts

[CQOI2009] 叶子的染色

能骗分,但不多。

解法

首先是部分分,看到了有链,直接输出2能得10pts。

最后就是正解。

\(dp_{i,state}\)表示以i为根的子树染成state颜色时的状态。

边界条件,如果当前是叶子节点,那么肯定只能让自己需要的颜色,而不要的就变成\(\infty\)

转移的时候就是

\[dp_{u,0} = \sum_{v \in son_u} min(dp_{v,1},dp_{v,0}-1) \]

\[dp_{u,1} = \sum_{v \in son_u} min(dp_{v,0},dp_{v,1}-1) \]

为什么呢。

因为如果当前节点要染\(state\)这种颜色,那么就可以让子树少染一次

代码

戳我
#include <bits/stdc++.h>
#define IAKIOI ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define pre(i, r, l) for (int i = r; i >= l; i--)
#define ls p << 1
#define rs p << 1 | 1
#define re read()
#define wr(n) write(n)
#define endl puts("")
#define sp putchar(' ')

const int N = 5e5 + 10, INF = 0x3f3f3f3f;

#define int long long

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef double db;

inline int read() {
	int f = 1, y = 0;
	char c = getchar();
	while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar();
	while (isdigit(c)) y = (y << 1) + (y << 3) + (c ^ 48), c = getchar();
	return y * f;
}

inline void write(int x) {
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

inline int max(int x, int y) {return x > y ? x : y;}
inline int min(int x, int y) {return x < y ? x : y;}

inline void swap(int &x, int &y) {int t = x;x = y, y = t;}
inline int pow(int x, int y) {
	int ans = 1;
	while (y) {
		if (y & 1) ans *= x;
		x *= x, y = y >> 1;
	}
	return ans;
}

int dp[N][2];
int col[N];
vector<int> g[N];
int n, m;

void dfs(int u, int fa) {
	if (u <= n) {
		dp[u][col[u]] = 1, dp[u][col[u] ^ 1] = INF;
		return ;
	}
	dp[u][0] = dp[u][1] = 1;
	for (auto v : g[u]) {
		if (v == fa) continue;
		dfs(v, u);
		dp[u][0] += min(dp[v][1], dp[v][0] - 1);
		dp[u][1] += min(dp[v][0], dp[v][1] - 1);
	}
}

signed main() {
	IAKIOI;
	m = re, n = re;
	rep(i, 1, n) col[i] = re;
	rep(i, 1, m - 1) {
		int u = re, v = re;
		g[u].push_back(v), g[v].push_back(u);
	}
	dfs(n + 1, 0);
	wr(min(dp[n + 1][0], dp[n + 1][1])), endl;
}

时间分配与预期

用了1hours,没推出来

预期:10pts
实际:10pts

[SDOI2011] 消防

还是不会

做法

还不会

代码

戳我
戳我干啥,说了不会

时间分配与预期

用了0.5hours,没想出来,所以没打

预期=实际=0pts

posted @ 2026-02-09 19:59  OiLight  阅读(20)  评论(0)    收藏  举报