POJ - 1815,字典序最小割集

Friendship

In modern society, each person has his own friends. Since all the people are very busy, they communicate with each other only by phone. You can assume that people A can keep in touch with people B, only if

  1. A knows B’s phone number, or
  2. A knows people C’s phone number and C can keep in touch with B.
    It’s assured that if people A knows people B’s number, B will also know A’s number.

Sometimes, someone may meet something bad which makes him lose touch with all the others. For example, he may lose his phone number book and change his phone number at the same time.

In this problem, you will know the relations between every two among N people. To make it easy, we number these N people by 1,2,…,N. Given two special people with the number S and T, when some people meet bad things, S may lose touch with T. Your job is to compute the minimal number of people that can make this situation happen. It is supposed that bad thing will never happen on S or T.

题意:有n个点,用矩阵形式表示出它们之间的边,当拆掉一个点时与之相邻的边全部被拆除,问现在想让两个点S,T不联通,至少要拆几个点(S,T不能拆),并输出这些点的标号,当有多组答案时输出字典序最小的那一组
思路:首先将非S,T的点i拆成左右两点i与i+n,两点间连一条容量为1的边保证这个点只会被拆一次,然后所有与i有连边的点u由u的右部点向i的左部点连边(即每个左部点只有入度,右部点只有出度,S,T的左部点和右部点相同,可以保证S,T不会被割开),跑S到T的最大流即为最小割
暴力做法: 现要求出字典序最小的割集,那么我们从小到大枚举所有点(除S,T),当枚举到i时将i与i+n之间的边拆除,跑最大流看流量是否减小,如果减小说明这个点在割集中,将它加入割集,反复进行上述操作(记得每次将已加入割集的点拆除)
优化方法: https://blog.csdn.net/xing_mo/article/details/103995004

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>//
#define MAXN 410
#define MAXM 40010
using namespace std;
const int INF = 0x3f3f3f3f;
int head[MAXN],tot;
struct edge
{
    int v,c,nxt;
}edg[MAXM << 1];
inline void addedg(int u,int v,int c)
{
    edg[tot].v = v;
    edg[tot].c = c;
    edg[tot].nxt = head[u];
    head[u] = tot++;
}
inline void add(int u,int v,int c)
{
    addedg(u,v,c);
    addedg(v,u,0);
}
int n,d[MAXN];
inline bool bfs(int s,int t)
{
    queue<int> qu;
    memset(d,-1,sizeof(int)*(n+n+2));
    qu.push(s);
    d[s] = 0;
    int v;
    while(!qu.empty())
    {
        int u = qu.front();
        qu.pop();
        for(int i = head[u];i != -1;i = edg[i].nxt)
        {
            v = edg[i].v;
            if(edg[i].c > 0 && d[v] == -1)
                d[v] = d[u]+1,qu.push(v);
        }
    }
    return d[t] != -1;
}
int dfs(int u,int flow,int t)
{
    if(u == t)
        return flow;
    int res = 0;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        int v = edg[i].v;
        if(edg[i].c > 0 && d[v] == d[u] + 1)
        {
            int tmp = dfs(v,min(flow,edg[i].c),t);
            flow -= tmp;
            res += tmp;
            edg[i].c -= tmp;
            edg[i^1].c += tmp;
            if(flow == 0)
                break;
        }
    }
    if(res == 0)
        d[u] = -1;
    return res;
}
inline void init()
{
    memset(head,-1,sizeof(int)*(n+n+2));
    tot = 0;
}
int mp[205][205],s,t,id[MAXN];
int main()
{
    while(~scanf("%d%d%d",&n,&s,&t))
    {
        for(int i = 1;i <= n;++i)
            for(int j = 1;j <= n;++j)
                scanf("%d",&mp[i][j]);
        if(mp[s][t])
        {
            printf("NO ANSWER!\n");
            continue;
        }
        init();
        for(int i = 1;i <= n;++i)
            for(int j = 1;j <= n;++j)
                if(mp[i][j]&&i!=s&&i != j)
                    add(i+n,j,1);
                else if(mp[i][j] && i != j)
                    add(i,j,1);
        for(int i = 1;i <= n;++i)
            if(i != s && i != t)
                id[i] = tot,add(i,i+n,1);
        int ans = 0;
        while(bfs(s,t))
            ans += dfs(s,INF,t);
        vector<int> ve;
        for(int i = 1;i <= n;++i)
        {
            if(i != s && i != t)
            {
                for(int j = 0;j < tot;++j)
                    if(j & 1)
                        edg[j].c = 0;
                    else
                        edg[j].c = 1;
                edg[id[i]].c = 0;
                for(int j = 0;j < ve.size();++j)
                    edg[id[ve[j]]].c = 0;
                int tmp = 0;
                while(bfs(s,t))
                    tmp += dfs(s,INF,t);
                if(tmp < ans)
                    ve.push_back(i),ans = tmp;
            }
        }
        printf("%d\n",ve.size());
        for(int i = 0;i < ve.size();++i)
            printf("%d ",ve[i]);
        printf("\n");
    }
    return 0;
}
/*
5 1 3
1 1 0 1 0
1 1 1 1 0
0 1 1 0 1
1 0 0 1 1
0 0 1 1 1

5 1 3
1 1 0 0 1
1 1 1 0 1
0 1 1 1 1
0 0 1 1 1
1 0 1 1 1

7 5 2
1 1 0 0 1 1 1
1 1 1 1 0 1 1
0 1 1 1 1 0 1
0 1 1 1 1 1 0
1 0 1 1 1 0 0
1 1 0 1 0 1 0
1 1 1 0 0 0 1
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值