牛客网
链接:https://ac.nowcoder.com/acm/problem/22598
来源:牛客网
- 题目:
时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format:%lld
题目描述 Rinne 最近了解了如何快速维护可支持插入边删除边的图,并且高效的回答一下奇妙的询问。 她现在拿到了一个 n 个节点 m
条边的无向连通图,每条边有一个边权 wi 现在她想玩一个游戏:选取一个 “重要点” S,然后选择性删除一些边,使得原图中所有除 S 之外度为
1 的点都不能到达 S。 定义删除一条边的代价为这条边的边权,现在 Rinne 想知道完成这个游戏的最小的代价,这样她就能轻松到达 rk1
了!作为回报,她会让你的排名上升一定的数量。
输入描述:
第一行三个整数 N,M,S,意义如「题目描述」所述。
接下来 M 行,每行三个整数 u,v,w 代表点 u 到点 v 之间有一条长度为 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≤S≤N≤105,M=N−1保证答案在 C++ long long 范围内
- 题解:
仔细观察题中给的数据m=n-1,其实就是给你一个树,而被删除度为1的点就是这个树的叶子节点,给你的s就是这个树的根
那一切就很清楚了:给你一个树和根节点,让你通过删边使得根节点与叶子节点不相连,问你怎么删值最小
这个是树形dp问题,我们要做的就是在搜索树的过程中不断处理数据
凡是dp问题,都有状态转移,就是一个大问题可以小问题
s为根节点时,要删去一些边让s与每个叶子节点不连通,其实就是让x为根节点的子树删去一些边,使得s和x的子树上每个叶子节点不连通。
两种情况:一个就是删去x与他儿子y的边
另一个就是看以y为子树的根节点的最小情况。
我们取较小值
f[x]+=min(f[y],edge[i].w);
edge[i].w是指当前节点x与其子节点y的距离
具体看代码吧
- 代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+3;
int f[maxn];
struct node{
int u,v,w,next;
}edge[maxn*2];
int root[maxn];
int head[maxn];
int cnt=0;
int n,m,s;
void addt(int u,int v,int w)
{
edge[++cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int x,int fa)
{
if(x!=s&&root[x]==1)f[x]=-1;//如果这个是叶子节点,就断绝与父亲的关系
//根节点s也有可能度为1,所以要除s之外
int now;//当前点x的子节点
//关系(fa-->x-->now)
for(int i=head[x];i;i=edge[i].next)
{
now=edge[i].v;
if(now==fa)continue;//因为是无向图,我们要防止返回到父亲节点
dfs(now,x);//继续向下
if(f[now]==-1)f[x]+=edge[i].w;//这个我们已经给断绝关系了,只加当前边的权值
else
f[x]+=min(f[now],edge[i].w);//考虑两个方面
}
}
int main()
{
cin>>n>>m>>s;
int u,v,w;
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
addt(u,v,w);
addt(v,u,w);//链式前向星
root[u]++;
root[v]++;
}
dfs(s,0);
printf("%d",f[s]);
return 0;
}