poj1815 无向图点的最小割

  这个题的意思是给你一个无向图, 其中有两个点S, T, 让你去掉图中最小数量的点使得S无法到达T, 不能去掉S或者T, 我们将一个顶点i拆成两个点2*i-1 -> 2*i, 边权为1, 对于一条边i - j, 那么就有2*i -> 2*j-1, 2*j -> 2*i-1, 边权为inf, 然后就是枚举边来求解最小割了, 代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

using namespace std;
const int inf = 0x3fffffff;
const int maxn = 200*2+100;

struct Dinic
{
    int n;       //n个顶点
    struct edge
    {
        int from, to, cap;
    };
    vector<int> G[maxn];
    vector<edge> e;
    int level[maxn], iter[maxn];

    void init()
    {
        for(int i=0; i<=n; i++) G[i].clear();
        e.clear();
    }

    void add_edge(int u, int v, int cap)
    {
        e.push_back((edge)
        {
            u, v, cap
        });
        e.push_back((edge)
        {
            v, u, 0
        });
        int m = e.size();
        G[u].push_back(m-2);
        G[v].push_back(m-1);
    }

    void bfs(int s)
    {
        memset(level, -1, sizeof(level));
        queue<int> que;
        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 &te = e[G[u][i]];
                if(te.cap>0 && level[te.to]<0)
                {
                    level[te.to] = level[u] + 1;
                    que.push(te.to);
                }
            }
        }
    }

    int dfs(int v, int t, int f)
    {
        if(v == t) return f;
        for(int &i=iter[v]; i<G[v].size(); i++)
        {
            edge &tpe = e[G[v][i]];
            if(tpe.cap>0 && level[v]<level[tpe.to])
            {
                int d = dfs(tpe.to, t, min(f, tpe.cap));
                if(d > 0)
                {
                    tpe.cap -= d;
                    e[G[v][i]^1].cap += d;
                    return d;
                }
            }
        }
        return 0;
    }

    int max_flow(int s, int t)
    {
        int flow = 0;
        for(;;)
        {
            bfs(s);
            if(level[t]<0) return flow;
            memset(iter, 0, sizeof(iter));
            int f;
            while((f=dfs(s, t, 0x3fffffff)) > 0)
                flow += f;
        }
    }
} di1, di2;
int N, S, T;

int main()
{
    scanf("%d%d%d", &N, &S, &T);
    di1.n = 2*N;
    di1.init();
    for(int i=1; i<=N; i++)
        di1.add_edge(2*i-1, 2*i, 1);
    bool flog = true;
    for(int i=1; i<=N&&flog; i++)
        for(int j=1; j<=N&&flog; j++)
        {
            int t;
            scanf("%d", &t);
            if(j>i && t==1)
            {
                //i -> j
                di1.add_edge(2*i, 2*j-1, inf);
                di1.add_edge(2*j, 2*i-1, inf);
                if((i==S&&j==T)||(j==S&&i==T)) flog=false;
            }
        }
    if(!flog)
    {
        printf("NO ANSWER!\n");
        return 0;
    }
    vector<int> ans;
    di2 = di1;
    int f = di2.max_flow(2*S, 2*T-1);
    for(int i=0; i<N; i++)     //N个顶点
    {
        if((i+1)==S || (i+1)==T) continue;
        di2 = di1;
        di2.e[2*i].cap = 0;
        int ff = di2.max_flow(2*S, 2*T-1);
        if(ff+1 == f)
        {
            ans.push_back(i+1);
            f -= 1;
            di1.e[2*i].cap = 0;
        }
    }
    printf("%d\n", ans.size());
    for(int i=0; i<ans.size(); i++)
        printf("%d%c", ans[i], i==ans.size()-1?‘\n‘:‘ ‘);
    return 0;
}
时间: 2024-10-13 22:28:16

poj1815 无向图点的最小割的相关文章

【POJ1815】Friendship 网络流最小割

题意:若干个人,然后给出s.t和连边矩阵,问最少搞死多少个人可以让s和t这两个(不可以被搞死)的人不连通.(还要字典序最小的方案) 题解:最小割,首先我们拆点,两部分流量为1来满足性质,然后连inf的联通边. 然后跑一遍就出来需要几个人了. 如果是inf就NO answer 然后我们从小到大枚举哪个人在最小割集里面,即把此人删掉再跑,看maxflow. 然后AC. 注意题意中说如果是需要杀人,第二行就输出这些人. 但是没说不需要时怎么办. 经过检测,输不输出空行都行,由此推测,没有这种情况. 代

求无向图最小割

先解释下名词的意思. 无向图的割:就是去掉一些边,使得原图不连通,最小割就是要去掉边的数量最小. 解决这个问题的常用办法就是Stoer-Wagner 算法: 先说下这个算法的步骤后面给出证明: 1.min=MAXINT,固定一个顶点P 2.从点P用类似prim的s算法扩展出"最大生成树",记录最后扩展的顶点和最后扩展的边 3.计算最后扩展到的顶点的切割值(即与此顶点相连的所有边权和),若比min小更新min 4.合并最后扩展的那条边的两个端点为一个顶点 5.转到2,合并N-1次后结束

Hdu 3691 Nubulsa Expo(无向图最小割)

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=3691 思路:无向图最小割模板题. 流量最小且汇点自定,则可在最小割T集中任选一点当做汇点. #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define debu using namespace std; const int maxn=350; const in

BZOJ1001[BeiJing2006]狼抓兔子(无向图最小割)

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1001 这题的题意其实就是求一个最小割,但是由于是无向图,所以加边的时候,两边的流量都要是输入的权值,然后就是一个dinic求一下最小割. 但是这题貌似有很高超的技巧来搞,可以把平面图上的最小割转成对偶图上的最短路来做,这样可以起到很明显的优化效果.现在还不是很明白,如果以后明白了,会再来更新. dinic: #include <cstdio> #include <cstring&g

无向图最小割 stoer_wagner算法

1 const int MAX_N = 1; 2 int G[MAX_N][MAX_N]; 3 int v[MAX_N]; // v[i]代表节点i合并到的顶点 4 int w[MAX_N]; // 定义w(A,x) = ∑w(v[i],x),v[i]∈A 5 bool visited[MAX_N]; // 用来标记是否该点加入了A集合 6 7 int stoer_wagner(int n) 8 { 9 int min_cut = inf; 10 for (int i = 0; i < n; +

POJ2914 (未解决)无向图最小割|Stoer-Wagner算法|模板

还不是很懂,贴两篇学习的博客: http://www.hankcs.com/program/algorithm/poj-2914-minimum-cut.html http://blog.sina.com.cn/s/blog_700906660100v7vb.html 算法步骤: 1. 设最小割cut=INF, 任选一个点s到集合A中, 定义W(A, p)为A中的所有点到A外一点p的权总和. 2. 对刚才选定的s, 更新W(A,p)(该值递增). 3. 选出A外一点p, 且W(A,p)最大的作为

poj1966Cable TV Network——无向图最小割(最大流)

题目:http://poj.org/problem?id=1966 把一个点拆成入点和出点,之间连一条边权为1的边,跑最大流即最小割: 原始的边权赋成inf防割: 枚举源点和汇点,直接相邻的两个点不必枚举: 注意:1.源点为枚举点i的出点,汇点为枚举点j的入点: 2.读入方式,免空格: 3.在dinic跑最大流的过程中,会改变边权,因此每次枚举都要复制一组边跑最大流,以免影响后面: 另:数据中的点从0开始,所以读入的时候++来使用. 代码如下: #include<iostream> #incl

利用Stoer-Wagner算法求无向图最小割

直接给出算法描述和过程实现: 算法步骤: 1. 设最小割cut=INF, 任选一个点s到集合A中, 定义W(A, p)为A中的所有点到A外一点p的权总和. 2. 对刚才选定的s, 更新W(A,p)(该值递增). 3. 选出A外一点p, 且W(A,p)最大的作为新的s, 若A!=G(V), 则继续2. 4. 把最后进入A的两点记为s和t, 用W(A,t)更新cut. 5. 新建顶点u, 边权w(u, v)=w(s, v)+w(t, v), 删除顶点s和t, 以及与它们相连的边. 6. 若|V|!=

USACO 5.4.3 点的最小割

这道题的难点在于将点拆分, 无向图变为有向图, 对于 i点我们可以将这个点拆分成2*i-1 -> 2*i,权值为1 对于i - j, 我们可以将点拆分以后再添加2*j -> 2*i-1 2*i -> 2*j-1,权值为inf,  然后求解最大流即为要去掉的顶点的个数, 求解具体的边的时候我们可以枚举要删除的边, 假设删除前的最大流为f,边权为1, 删除边后的最大流为ff, 若ff+1 == f的话那么这条边就在最小割集中.然后更新最大流,删掉最这条边重复上述操作即可, 代码如下: /*