题意:
给定一棵有 n n n 个结点的树,开始时树的结点和边均为白色,现要求将结点和边全部染黑,可以选择一个结点,将其直接染黑,代价为 1 1 1,结果为该结点及其邻接结点、边均被染黑。对于其他结点递归地考虑:① 若某条边被染黑,则两端结点被染黑;② 若某条边两端结点被染黑,则该边被染黑;③ 若某个点度为 k > 1 k \gt 1 k>1,且其中 k − 1 k - 1 k−1 条边被染黑,则剩余的那条边也被染黑。求最小代价将树的结点和边均染黑。 ( n ≤ 1 0 5 ) (n \leq 10^5) (n≤105)
链接:
https://vjudge.net/problem/Gym-102460B
解题思路:
贪心,然后发现还是要树形 dp。树形
d
p
dp
dp,
d
p
[
u
]
[
0
/
1
/
2
]
[
0
/
1
]
dp[u][0/1/2][0/1]
dp[u][0/1/2][0/1],其中
0
/
1
/
2
0/1/2
0/1/2 表示三种被染黑的点:
0
0
0 表示为下方的黑点传递而被染黑,
1
1
1 表示被直接染黑,
2
2
2 表示为上方的黑点传递而被染黑。
0
/
1
0/1
0/1 表示子树内是否缺一条边被染黑。
转移:
d
p
[
u
]
[
1
]
[
0
]
dp[u][1][0]
dp[u][1][0] 最好考虑,直接为
∑
d
p
[
v
]
[
0
/
1
/
2
]
[
0
/
1
]
+
1
\sum{dp[v][0/1/2][0/1]} + 1
∑dp[v][0/1/2][0/1]+1
d
p
[
u
]
[
2
]
[
0
]
=
∑
d
p
[
v
]
[
0
]
[
1
]
dp[u][2][0] = \sum{dp[v][0][1]}
dp[u][2][0]=∑dp[v][0][1],不能由
d
p
[
v
]
[
0
]
[
0
]
dp[v][0][0]
dp[v][0][0] 转移(
00
00
00 状态下父边会被染黑)
d
p
[
u
]
[
2
]
[
1
]
=
d
p
[
u
]
[
2
]
[
0
]
dp[u][2][1] = dp[u][2][0]
dp[u][2][1]=dp[u][2][0] 状态下,其中一棵子树状态替换为
20
/
21
20/21
20/21,取最优
d
p
[
u
]
[
0
]
[
0
]
=
dp[u][0][0] =
dp[u][0][0]= 至少一棵子树状态为
00
/
10
00/10
00/10(状态含义,被下方传递染黑)+ 其他子树取
m
i
n
{
00
,
01
,
10
}
min\{00, 01, 10\}
min{00,01,10}
d
p
[
u
]
[
0
]
[
1
]
=
d
p
[
u
]
[
0
]
[
0
]
dp[u][0][1] = dp[u][0][0]
dp[u][0][1]=dp[u][0][0] 状态下,其中的“其他子树“中的一棵子树状态替换为
20
/
21
20/21
20/21
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int inf = maxn;
const int mod = 1e9 + 7;
vector<int> G[maxn];
int deg[maxn];
ll dp[maxn][3][2];
int n;
void dfs(int u, int f){
for(int i = 0; i < 3; ++i){
for(int j = 0; j < 2; ++j){
dp[u][i][j] = inf;
}
}
dp[u][1][0] = 1, dp[u][2][0] = 0;
if(deg[u] == 1 && f) return;
dp[u][0][0] = dp[u][0][1] = dp[u][2][0] = dp[u][2][1] = 0;
ll tp0[2] = {0, inf}, tp1[2][2] = {0, inf, inf, inf};
for(auto &v : G[u]){
if(v == f) continue;
dfs(v, u);
ll tmp = min(dp[v][1][0], min(dp[v][0][0], dp[v][0][1]));
tp0[1] = min(tp0[1] + tmp, tp0[0] + min(dp[v][0][0], dp[v][1][0])), tp0[0] += tmp;
tp1[1][1] = min(tp1[1][1] + tmp, min(tp1[0][1] + min(dp[v][2][0], dp[v][2][1]), tp1[1][0] + min(dp[v][0][0], dp[v][1][0])));
tp1[0][1] = min(tp1[0][1] + tmp, tp1[0][0] + min(dp[v][0][0], dp[v][1][0]));
tp1[1][0] = min(tp1[1][0] + tmp, tp1[0][0] + min(dp[v][2][0], dp[v][2][1]));
tp1[0][0] += tmp;
dp[u][1][0] += min(min(dp[v][0][0], dp[v][0][1]), min(dp[v][1][0], min(dp[v][2][0], dp[v][2][1])));
dp[u][2][1] = min(dp[u][2][0] + min(dp[v][2][0], dp[v][2][1]), dp[u][2][1] + dp[v][0][1]);
dp[u][2][0] += dp[v][0][1];
}
dp[u][0][0] = tp0[1], dp[u][0][1] = tp1[1][1];
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n;
for(int i = 1; i < n; ++i){
int u, v; cin >> u >> v;
G[u].pb(v), G[v].pb(u);
++deg[u], ++deg[v];
}
dfs(1, 0);
cout << min(dp[1][1][0], min(dp[1][0][0], dp[1][0][1])) << endl;
return 0;
}
专门练了很多树形
d
p
dp
dp 的题目,就是为了搞定这道。尽管还是花了整整一天,也值了,哈哈。