<题目链接>
题目大意:
一个 $n$ 个节点 $m$ 条边的无向连通图,每条边有一个边权 $w_i$。现在她想玩一个游戏:选取一个 “重要点” S,然后选择性删除一些边,使得原图中所有除 S 之外度为 1 的点都不能到达 S。定义删除一条边的代价为这条边的边权,现在 Rinne 想知道完成这个游戏的最小的代价。(2≤S≤N≤10^5,M=N−1,保证答案在 C++ long long 范围内)
解题分析:
因为该无向图连通,并且$n=m+1$,所以该图一定是一颗树。我们可以用树形DP解决本题。题目意思就是要我们删除该树中除$u$以外的所有叶子节点(如果u不为叶子节点的话,就是删除所有的叶子节点)的最小代价。很容易想到转移方程,用$cost[u]$表示删除以u为根的子树中所有叶子的最小代价,如果v直接与叶子相连的话,肯定要删除v--->叶子的这条边,所以所有的叶子节点的点权初始化为无穷大。然后就是利用树中的关系转移,对于以$u$为根的子树来说,$v$是它的一个直接相连的子节点,如果以v为根的子树中有叶子,则有两种情况删除。一:直接删除$u-->v$直接相连的这条边;二:不删除$u-->v$的这条边,删除以$v$为根的子树中的边,使v中的叶子节点不可到达$u$节点。这两种情况取最小值即可。同时本题用最大流也能够很容易解决。
树形DP
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+5; struct Edge{int to;ll w;}; vector<Edge>G[N]; int n,m,s; ll cost[N]; void dfs(int u,int fa){ if(G[u].size()==1 && u!=s) //叶子节点的权值置为无穷 cost[u]=1e18; for(int i=0;i<G[u].size();i++){ int v=G[u][i].to; if(v==fa)continue; dfs(v,u); cost[u]+=min(cost[v],G[u][i].w); //就是要删除以v为根的子树中的叶子所有叶子节点的最小代价 } } int main(){ scanf("%d%d%d",&n,&m,&s); for(int i=1;i<=m;i++){ int u,v;ll w;scanf("%d%d%lld",&u,&v,&w); G[u].push_back(Edge{v,w}); G[v].push_back(Edge{u,w}); } dfs(s,0); printf("%lld ",cost[s]); }
最大流Dinic
以S为汇点,再建立一个超级源点,连上所有的叶子节点(除S以外),然后直接跑一遍最大流即可。
下面的是两种最大流的板子:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+5; const ll INF = 1e18; int n, m, S; int deg[N]; struct Dinic { struct edge{ int from,to;ll cap,flow; }; //cap-flow才是这条边的真实流量 vector<edge>es; vector<int>G[N]; bool vis[N]; int dist[N],iter[N]; void init(int n){ for(int i=0; i<=n+10; i++)G[i].clear(); es.clear(); } void addedge(int from,int to,ll cap){ es.push_back((edge){from,to,cap,0}); //将边存储的边表 es.push_back((edge){to,from,0,0}); int x=es.size(); G[from].push_back(x-2); //G[u][i]记录以u为顶点的第i条边的反边在es中的编号 G[to].push_back(x-1); } bool BFS(int s,int t){ //bfs将该图划分成分层图 memset(vis,0,sizeof(vis)); queue <int> q; vis[s]=1; dist[s]=0; q.push(s); while(!q.empty()){ int u=q.front();q.pop(); for(int i=0; i<G[u].size(); i++){ edge &e=es[G[u][i]]; if(!vis[e.to]&&e.cap>e.flow){ vis[e.to]=1; dist[e.to]=dist[u]+1; q.push(e.to); } } } return vis[t]; } int DFS(int u,int t,ll f){ if(u==t||f==0)return f; int nowflow=0,d; for(int &i=iter[u]; i<G[u].size(); i++){ edge &e=es[G[u][i]]; if(dist[u]+1==dist[e.to]&&(d=DFS(e.to,t,min(f,e.cap-e.flow)))>0){ e.flow+=d; //正边真实流量-d es[G[u][i]^1].flow-=d; //反边真实流量+d nowflow+=d; //得到现在搜得的能够流入汇点的流量 f-=d; //找到一条增广路之后,减去这条路的流量,然后继续从这个顶点的其它边开始寻找增广路 if(f==0)break; } } return nowflow; } int Maxflow(int s,int t){ int flow=0; while(BFS(s,t)){ memset(iter,0,sizeof(iter)); int d=0; while(d=DFS(s,t,INF))flow+=d; } return flow; } }dinic; void Get_Graph(){ scanf("%d %d %d", &n, &m, &S); for (int i = 1; i <= m; ++i) { int u, v; ll w; scanf("%d %d %lld", &u, &v, &w); deg[u]++; deg[v]++; dinic.addedge(u, v, w); dinic.addedge(v, u, w); } for (int i = 1; i <= n; ++i) if (deg[i] == 1 && i != S) dinic.addedge(0, i, INF); //建一个超级源点,流向所有度数为1的点(除s以外) } int main(){ Get_Graph(); cout<<dinic.Maxflow(0,S)<<endl; }
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 100000 + 5; const ll INF = 1e18; int n, m, S; int deg[N]; struct edge { int to, rev; ll cap; edge(int _to, ll _cap, int _rev) { to = _to, cap = _cap, rev = _rev; } }; vector<edge> G[N]; inline void add_edge(int from, int to, ll cap) { G[from].push_back(edge(to, cap, G[to].size())); G[to].push_back(edge(from, 0, G[from].size() - 1)); } void init() { scanf("%d %d %d", &n, &m, &S); for (int i = 1; i <= m; ++i) { int u, v; ll w; scanf("%d %d %lld", &u, &v, &w); deg[u]++; deg[v]++; add_edge(u, v, w); add_edge(v, u, w); } for (int i = 1; i <= n; ++i) if (deg[i] == 1 && i != S) add_edge(0, i, INF); //建一个超级源点,流向所有度数为1的点(除s以外) } struct Dinic { int level[N], iter[N]; queue <int> que; inline void bfs(int s) { memset(level, -1, sizeof level); level[s] = 0; //将图划分为层次图 que.push(s); while (!que.empty()) { int u = que.front(); que.pop(); for (int i = 0; i < G[u].size(); ++i) { edge &e = G[u][i]; if (e.cap > 0 && level[e.to] < 0) { level[e.to] = level[u] + 1; que.push(e.to); } } } } ll dfs(int u, int t, ll f) { if (u == t) return f; for (int &i = iter[u]; i < G[u].size(); ++i) { edge &e = G[u][i]; if (e.cap > 0 && level[e.to] > level[u]) { ll d = dfs(e.to, t, min(f, e.cap)); if (d > 0) { e.cap -= d; G[e.to][e.rev].cap += d; return d; } } } return 0; } ll max_flow(int s, int t) { ll flow = 0; for (;;) { memset(iter, 0, sizeof iter); bfs(s); if (level[t] < 0) return flow; ll f; while ((f = dfs(s, t, INF)) > 0) flow += f; } } }dinic; int main() { init(); cout << dinic.max_flow(0, S) << endl; //S为汇点 }