题意:
给定一棵有 个结点的树,开始时树的结点和边均为白色,现要求将结点和边全部染黑,可以选择一个结点,将其直接染黑,代价为 ,结果为该结点及其邻接结点、边均被染黑。对于其他结点递归地考虑:① 若某条边被染黑,则两端结点被染黑;② 若某条边两端结点被染黑,则该边被染黑;③ 若某个点度为 ,且其中 条边被染黑,则剩余的那条边也被染黑。求最小代价将树的结点和边均染黑。
链接:
https://vjudge.net/problem/Gym-102460B
解题思路:
贪心,然后发现还是要树形 dp。树形
,
,其中
表示三种被染黑的点:
表示为下方的黑点传递而被染黑,
表示被直接染黑,
表示为上方的黑点传递而被染黑。
表示子树内是否缺一条边被染黑。
转移:
最好考虑,直接为
,不能由
转移(
状态下父边会被染黑)
状态下,其中一棵子树状态替换为
,取最优
至少一棵子树状态为
(状态含义,被下方传递染黑)+ 其他子树取
状态下,其中的“其他子树“中的一棵子树状态替换为
参考代码:
#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;
}
专门练了很多树形
的题目,就是为了搞定这道。尽管还是花了整整一天,也值了,哈哈。