题目链接:https://ac.nowcoder.com/acm/problem/22598
简单题意:给定一棵有边权的树和一个点S,删去一些边使得所有的叶子和S不连通。求删去的最小边权和
可以把S看做根。f[i]表示使得i的子树上的叶子和i不连通,删去的最小边权和。则有f[i]=Σmin(f[j],dis[i][j]),其中j是i的子结点。这题的n<=1e6,不能直接用dis数组存边权,可以用邻接表顺便存下来。最后答案就是f[s]
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int maxn=1e5+10;
struct st{int ver,w;};
vector<st> g[maxn];
int t,n,m,s,i,j,k,vis[maxn];
ll f[maxn];
void dp(int u){
vis[u]=1; int num=0;
for (int i=0;i<g[u].size();i++) //判断叶子
if (vis[g[u][i].ver]==0) num++;
if (num==0) f[u]=1e10;
for (int i=0;i<g[u].size();i++){
int k=g[u][i].ver; //子节点
if (vis[k]) continue;
dp(k);
if (f[k]>g[u][i].w) f[u]+=g[u][i].w;else f[u]+=f[k];
}
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for (i=1;i<n;i++){
int x,y,w; st e;
scanf("%d%d%d",&x,&y,&w); e.w=w;
e.ver=x; g[y].pb(e);
e.ver=y; g[x].pb(e);
}
dp(s);
printf("%lld
",f[s]);
return 0;
}
一道类似的题:牛客nc210473
题目链接:https://ac.nowcoder.com/acm/problem/210473
简单题意:删去一些边,使得叶子和根节点1不连通。求删去的边权和<=m的情况下,其中最大边权的最小值
要求最小的最大值,考虑二分答案mid,然后用上一题的树形dp来判断最大值为mid的情况下,结果f[1]和m的关系即可。注意做树形dp的时候如果边权>mid就不能删掉这个边,只能删掉子树
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
vector<int> g[1010];
int n,m,i,l,r,mid,d[1010][1010],vis[1010],f[1010];
void dp(int u){
vis[u]=1; int num=0;
for (int i=0;i<g[u].size();i++)
if (!vis[g[u][i]]) num++;
if (num==0) f[u]=1e6;
for (int i=0;i<g[u].size();i++){
int k=g[u][i];
if (vis[k]) continue;
dp(k);
if (d[u][k]>mid) f[u]+=f[k]; else f[u]+=min(f[k],d[u][k]);
}
}
int main(){
cin>>n>>m;
for (i=1;i<n;i++){
int x,y,dis;
cin>>x>>y>>dis;
g[x].pb(y);g[y].pb(x);
d[x][y]=d[y][x]=dis;
}
l=0;r=1e3+10;
while (l<r-1){
memset(vis,0,sizeof(vis)); memset(f,0,sizeof(f));
mid=(l+r)/2; dp(1);
if (f[1]>m) l=mid; else r=mid;
}
if (r==1e3+10) cout<<-1<<endl; else cout<<r<<endl;
return 0;
}