• Nowcoder contest 370F Rinne Loves Edges (简单树形DP) || 【最大流】(模板)


    <题目链接>

    题目大意:

    一个 $n$ 个节点 $m$ 条边的无向连通图,每条边有一个边权 $w_i$。现在她想玩一个游戏:选取一个 “重要点” S,然后选择性删除一些边,使得原图中所有除 S 之外度为 1 的点都不能到达 S。定义删除一条边的代价为这条边的边权,现在 Rinne 想知道完成这个游戏的最小的代价。(2SN10^5,M=N1,保证答案在 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为汇点
    }
    不能记录边的容量
  • 相关阅读:
    C++易错处总结
    Dev-C++debug使用方法
    IDEA使用心得
    记录零碎ACM小知识
    Div3 C good number easy version
    cin,scanf后使用getline() 函数的易错点
    while中同时使用scanf和break的易错点
    聚集表索引优化
    .net中不能在DropDownList中选中多个项的解决方法
    MVC3 带查询的分页Helper
  • 原文地址:https://www.cnblogs.com/00isok/p/10508287.html
Copyright © 2020-2023  润新知