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

A knows B’s phone number, or
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/103991994
优化方法:同样需要从小到大枚举所有点(除S,T),当枚举到i时将i与i+n之间的边拆除,然后看最大流是否减小,但拆除一条边相当于将i与i+n之间的边容量减一,可以在残余网络中这条边容量是否为0,不为0的话将这条边容量减小不影响最大流,若为0则在残余网络中跑一条i到i+n的路径,如果存在,即使将原来那条边删去,可以沿这条路径增广将流量补回来,即这条边不是割边,但如果不存在,那么流量将会减小1,此时需要去除i - i+n这条边流量的影响,即对i - s与t - i+n之间的路径各进行一次增广

(增广部分自己是第一次写,不知道有没有什么更好的写法,各位大佬教教我)

#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 vis[MAXN],path[MAXN],pre[MAXN];//vis用来标记这个点是否到过,pre表示从那个点到的这个点,path为到这个点的边的下标
inline bool findpath(int fr,int ed)
{
    memset(vis,0,sizeof(int)*(n+n+2));
    queue<int> qu;
    qu.push(fr);
    vis[fr] = 1;
    int u,v;
    while(!qu.empty())
    {
        u = qu.front();
        qu.pop();
        for(int i = head[u];i != -1;i = edg[i].nxt)
        {
            v = edg[i].v;
            if(!vis[v] && edg[i].c > 0)
            {
                vis[v] = 1;
                qu.push(v);
                pre[v] = u;
                path[v] = i;
                if(v == ed)
                    return true;
            }
        }
    }
    return false;
}

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)
            {
                if(edg[id[i]].c-1 >= 0) //如果(u,v)容量大于1,则这条边容量减一最大流也不会改变,非割边
                    continue;
                else
                {
                    if(findpath(i,i+n)) //如果残留网络中还存在其它路径使得u点可以到达v号点,则将这条边容量减一后可以沿那条路径增广
                        continue;
                    else //该边容量减一后最大流减小,需对u->s,v->t的路径进行增广
                    {
                        findpath(i,s);
                        int u = s;
                        while(u != i)
                        {
                            edg[path[u]].c -= 1;
                            edg[path[u]^1].c += 1;
                            u = pre[u];
                        }
                        findpath(t,i+n);
                        u = i+n;
                        while(u != t)
                        {
                            edg[path[u]].c -= 1;
                            edg[path[u]^1].c += 1;
                            u = pre[u];
                        }
                        ve.push_back(i);
                    }
                }
            }
        }
        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
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值