树形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

浙公网安备 33010602011771号