题目链接: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; }