首页 > 其他 > 详细

最大流与最小割

时间:2020-06-08 22:04:27      阅读:45      评论:0      收藏:0      [点我收藏+]

1、POJ 3713 Transferring Sylla

题意:判断一个无向图是否三连通?

思路:枚举每个点,删去后用Tarjan判断图中是否存在割点,如果存在则该图不满足三连通性。

// #include<bits/stdc++.h>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring> // for memset
#include <vector> // vector<int>().swap(v);

#define IOS ios_base::sync_with_stdio(0); cin.tie(0)
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;

int del,root;
bool cut;
int dfn[510], low[510];

vector<int> e[510];
int n,m;
int tot;

void Tarjan(int u,int p)
{
    if(cut) return;
    dfn[u]=low[u]= ++tot;
    int son=0;
    for(vector<int>::iterator it=e[u].begin();it!=e[u].end();it++)
    {
        int v=*it;
        if(v==p || v==del)    continue;
        if(!dfn[v])
        {
            son++;
            Tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if((u == root && son > 1) || (u != root && low[v] >= dfn[u]))
            {
                cut=true;
                return;
            }
        }else{
            low[u]=min(low[u],dfn[v]);
        }
    }
}

int main(void)
{
    while (scanf("%d%d", &n, &m) != EOF && n) {
        for (int i = 0; i < n; ++i) e[i].clear();
        for (int i = 0; i < m; ++i) {
            int u, v;
            scanf("%d%d", &u, &v);
            e[u].push_back(v);
            e[v].push_back(u);
        }
        cut = 0;
        for (int i = 0; i < n; ++i) {
            del = i;
            memset(dfn, 0, sizeof(dfn));
            tot = 0;
            root = !i;

            Tarjan(root, -1);
            if (cut) break;
            for (int j = 0; j < n; ++j) {
                if (j != del && !dfn[j]) {
                    cut = 1;
                    break;
                }
            }
            if (cut) break;
        }
        printf("%s\n", cut ? "NO" : "YES");
    }

    return 0;
}

 

2、POJ 2987 Firing

题意:老板决定大裁员,每开除一个人,同时要将其下属一并开除,以及下属的下属... ... 给出开除掉每个人的贡献值和各种从属关系,求最小裁员数及最大贡献值和。

定义一个有向图 G=(V,E) 的闭合图是该有向图的一个点集,且该点集的所有出边都还指向该点集。即闭合图内的任意点的任意后继也一定在闭合图中。
// 最大权闭合图的求解方法是:

1.先构造网络流N,添加源点s,从s到正权值点做一条边,容量为点的权值。

2.添加汇点t,从负权值点到t做一条边,容量为点的权值的绝对值。

3.原来的边的容量统统设为无穷大。

4.求解最小割,最大权=正权值之和-最小割权值

5.残余网络中的点的个数即为裁员个数。

感觉网络流不是很好理解,这个题先码上过段时间再来理解

 

// #include<bits/stdc++.h>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring> // for memset
#include <vector> // vector<int>().swap(v);
#include <set> // multiset set<int,greater<int>> //big->small
#include <map>
#include <stack> // top()
#include <queue> // front() // priority_queue<T,vector<T>,greater<T> >
#include <cmath> // auto &Name : STLName  Name.

using namespace std;
typedef long long ll;

struct edge
{
    int to,rev;
    ll cap;
    edge(int to,ll cap, int rev) :to(to), cap(cap), rev(rev){}
};

vector<edge> G[5015];
int level[5015];
int iter[5015];

void add_edge(int from, int to, int cap)
{
    G[from].push_back(edge(to, cap, G[to].size() ));
    G[to].push_back(edge(from, 0, G[from].size() - 1));
}

void bfs(int s)
{
    memset(level, -1, sizeof(level));
    queue<int> que;
    level[s] = 0;
    que.push(s);
    while (!que.empty())
    {
        int v = que.front(); que.pop();
        for (int i = 0; i < G[v].size(); ++i)
        {
            edge& e = G[v][i];
            if (e.cap > 0 && level[e.to] < 0)
            {
                level[e.to] = level[v] + 1;
                que.push(e.to);
            }
        }
    }
}

ll dfs(int v, int t, ll f)
{
    if (v == t){ return f;}
    for (int& i = iter[v]; i < G[v].size(); ++i)
    {
        edge& e = G[v][i];
        if (e.cap > 0 && level[v] < level[e.to])
        {
            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 (;;)
    {
        bfs(s);
        if (level[t] < 0){ return flow; }
        memset(iter, 0, sizeof(iter));
        ll f;
        while ((f = dfs(s, t, 0x3f3f3f3f3f3f3f3f)) > 0)
        {
            flow += f;
        }
    }
}

int vertex_count, visited[MAX_V];
// 遍历残余网络
void solve(int v)
{
    ++vertex_count;
    visited[v] = true;
    for (int i = 0; i < int(G[v].size()); ++i) 
    {
        const edge &e = G[v][i];
        if (e.cap > 0 && !visited[e.to]) 
        {
            solve(e.to);
        }
    }
}

int main(void)
{
    int n, m, w;
    ll W = 0;
    scanf("%d%d", &n, &m);
    const int s = 0, t = n + 1;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &w);
        if (w > 0)
        {
            W += w;
            add_edge(s, i, w);
        }
        if (w < 0)
        {
            add_edge(i, t, -w);
        }
    }
 
    int u, v;
    for (int i = 0; i < m; ++i)
    {
        scanf("%d%d", &u, &v);
        add_edge(u, v, 0x3f3f3f3f3f3f3f3f);
    }
 
    ll max_profit = W - max_flow(s, t);
    solve(s);
    printf("%d %I64d\n", --vertex_count, max_profit);

    return 0;
}

 

3、POJ 2914 Minimum Cut

题意:求无向图的最小割。

思路:Stoer-Wagner算法

 哎这道题还是一头雾水,还是先码上,滚去复习图论的知识点了 555 菜到无法呼吸

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
     
    #define MAX_N 500 + 16
    #define INF 0x3f3f3f3f
     
    int G[MAX_N][MAX_N];
    int v[MAX_N];            //    v[i]代表节点i合并到的顶点
    int w[MAX_N];            //    定义w(A,x) = ∑w(v[i],x),v[i]∈A
    bool visited[MAX_N];    //    用来标记是否该点加入了A集合
     
    int stoer_wagner(int n)
    {
        int min_cut = INF;
        for (int i = 0; i < n; ++i)
        {
            v[i] = i;        //    初始还未合并,都代表节点本身
        }
        
        while (n > 1)
        {
            int pre = 0;    //    pre用来表示之前加入A集合的点(在t之前一个加进去的点)
            memset(visited, 0, sizeof(visited));
            memset(w, 0, sizeof(w));
            for (int i = 1; i < n; ++i)
            {
                int k = -1;
                for (int j = 1; j < n; ++j)  //    选取V-A中的w(A,x)最大的点x加入集合
                {
                    if (!visited[v[j]])
                    {
                        w[v[j]] += G[v[pre]][v[j]];
                        if (k == -1 || w[v[k]] < w[v[j]])
                        {
                            k = j;
                        }
                    }
                }
                
                visited[v[k]] = true;        //    标记该点x已经加入A集合
                if (i == n - 1)                //    若|A|=|V|(所有点都加入了A),结束
                {
                    const int s = v[pre], t = v[k];        //    令倒数第二个加入A的点(v[pre])为s,最后一个加入A的点(v[k])为t
                    min_cut = min(min_cut, w[t]);        //    则s-t最小割为w(A,t),用其更新min_cut
                    for (int j = 0; j < n; ++j)            //    Contract(s, t)
                    {
                        G[s][v[j]] += G[v[j]][t];
                        G[v[j]][s] += G[v[j]][t];
                    }
                    v[k] = v[--n];                        //    删除最后一个点(即删除t,也即将t合并到s)
                }
                // else 继续
                pre = k;
            }
        }
        return min_cut;
    }
     
     

    int main()
    {
        int n, m;
        while (scanf("%d%d", &n, &m) != EOF)
        {
            memset(G, 0, sizeof(G));
            while (m--)
            {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                G[u][v] += w;
                G[v][u] += w;
            }
            printf("%d\n", stoer_wagner(n));
        }
        return 0;
    }

 

最大流与最小割

原文:https://www.cnblogs.com/jaszzz/p/13061936.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!