链接:https://ac.nowcoder.com/acm/problem/22598
题解:https://ac.nowcoder.com/discuss/396415
初次接触树形dp
题目描述
Rinne 最近了解了如何快速维护可支持插入边删除边的图,并且高效的回答一下奇妙的询问。
她现在拿到了一个 n n n 个节点 m m m 条边的无向连通图,每条边有一个边权 w i w_i wi
现在她想玩一个游戏:选取一个 “重要点” S,然后选择性删除一些边,使得原图中所有除 S 之外度为 1 的点都不能到达 S。
定义删除一条边的代价为这条边的边权,现在 Rinne 想知道完成这个游戏的最小的代价,这样她就能轻松到达 rk1 了!作为回报,她会让你的排名上升一定的数量。
输入描述:
第一行三个整数 N , M , S N,M,S N,M,S,意义如「题目描述」所述。
接下来 M M M 行,每行三个整数 u , v , w u,v,w u,v,w 代表点 u u u 到点 v v v 之间有一条长度为 w w w 的无向边。
输出描述:
一个整数表示答案。
示例1
输入
4 3 1
1 2 1
1 3 1
1 4 1
输出
3
说明
需要使得点 2,3,4 不能到达点 1,显然只能删除所有的边,答案为 3
示例2
输入
4 3 1
1 2 3
2 3 1
3 4 2
输出
1
说明
需要使得点 4 不能到达点 1,显然删除边 2 ↔ 3 2 \leftrightarrow 3 2↔3是最优的。
备注:
2 ≤ S ≤ N ≤ 1 0 5 , M = N − 1 2\le S \le N\le 10^{5} ,M = N-1 2≤S≤N≤105,M=N−1,保证答案在 C++ long long 范围内。
思路:
注意备注中说了 M = N - 1 ,而且是无向连通图,所以这是一棵树。那么这棵树中度为1的点就是叶子结点了。
现在问题就变成了:在以S为根的树上删掉权值和尽量小的一些边使得根与每一个叶子节点都不连通。
分析子问题:以 u 为根的子树的叶子结点与 u 不连通的方法有两种,删掉子节点直接与 u 相连的那条边,或者保留子节点到 u 的边,删除以子节点为根时不连通的最小代价方法。两种方法取最小值。
设 f i f_i fi 表示以 i i i 为根节点的最小代价,状态转移方程: f i = ∑ min ( f j , w i , j ) f_i=\sum \min (f_j,w_{i,j}) fi=∑min(fj,wi,j) 。
Code:
#include
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
ll N, M, S;
vector<pair<ll, ll>> e[100005];
ll deg[100005], f[100005];
void dfs(int cur, int fa) {
if (deg[cur] == 1 && cur != S) {
f[cur] = INF;
return;
}
for (int i = 0; i < e[cur].size(); i++) {
int v = e[cur][i].first;
if (v != fa) {
dfs(v, cur);
f[cur] += min(f[v], e[cur][i].second);
}
}
}
int main() {
cin >> N >> M >> S;
ll u, v, w;
for (int i = 1; i <= M; i++) {
cin >> u >> v >> w;
e[u].push_back(make_pair(v, w));
e[v].push_back(make_pair(u, w));
deg[u]++;
deg[v]++;
}
dfs(S, 0);
cout << f[S] << endl;
return 0;
}
#include
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int deg[N];
ll dp[N];
int head[N], cnt;
struct edge {
int v, w, next;
} e[N * 2];
int n, m, s;
void add(int u, int v, int w) {
e[cnt] = {v, w, head[u]};
head[u] = cnt++;
e[cnt] = {u, w, head[v]};
head[v] = cnt++;
}
void dfs(int u, int fa) {
if (deg[u] == 1 && u != s) {
dp[u] = INF;
return;
}
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v, w = e[i].w;
if (v == fa)
continue;
dfs(v, u);
dp[u] += min(1LL * w, dp[v]);
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m >> s;
memset(head, -1, sizeof(head));
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
add(u, v, w);
deg[u]++, deg[v]++;
}
dfs(s, s);
cout << dp[s] << endl;
return 0;
}