[20180325 hu测]舞动的夜晚(最大流+Tarjan缩点)

题目

传送门
Contest Hunter 是个好地方
这里写图片描述

题解

首先感谢@xp学长的讲解
这是道网络流好题;
原来理解错了题意,然后WA的非常惨。并不是跑一个sb最大流输出方案;
也就是使二分图最大匹配减少的边的数量;
算法如下:
我们先随便跑出一个最大流,在残余网络上进行tarjan找强连通分量缩点,因为在一个无向图中的强联通分量意味这可以相互通达,割掉其中一条边后并不会影响到汇点的路径;结合下图理解一下;
这里写图片描述
跑完最大流之后图是这个样子;
这里写图片描述
我们会发现其中1、2 、4、5构成了一个强联通分量
也就是说强联通分量里的边如果删掉之后一定可以通过其他的边到达,此时片可以把他们都看成一个点。
这里写图片描述
特别需要注意的是源点和汇点也需要跑Tarjan,因为如果有的点因为它的边已经满流而无法通过时,会和源点构成强联通分量。

代码

代码写的非常丑和冗长;

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int maxn=500001;
const int inf=1e9;

int n,m,num,maxflow;
struct Edge{
    int next,to,dis,from,id;
}edge[maxn<<1];
int num_edge=-1,head[maxn],cur[maxn],deep[maxn];
int cnt,dfn[maxn],low[maxn],zhan[maxn],top,col[maxn],num_col;
bool ifin[maxn];
queue <int> q;

int read()
{
    char ch=getchar(); int now=0,f=1;
    while (ch<'0' || ch>'9') {ch=getchar(); if (ch=='-') f=-1;}
    while (ch>='0'&&ch<='9')
    {
        now=(now<<1)+(now<<3)+ch-'0';
        ch=getchar();
    }
    return now*f;
}

void add_edge(int from,int to,int dis,int id)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].dis=dis;
    edge[num_edge].from=from;
    edge[num_edge].id=id;
    edge[num_edge].to=to;
    head[from]=num_edge;
}
void add(int x,int y,int z,int id) {add_edge(x,y,z,id); add_edge(y,x,0,id);}

bool bfs(int s,int t)
{
    memset(deep,0x7f,sizeof(head));
    while (!q.empty()) q.pop();
    for (int i=0; i<=t; i++) cur[i]=head[i];
    q.push(s); deep[s]=0;

    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i!=-1; i=edge[i].next)
        {
            int to=edge[i].to;
            if (deep[to]>inf && edge[i].dis)
            {
                deep[to]=deep[now]+1;
                if (to==t) return 1;
                q.push(to);
            }
        }
    }
    return deep[t]<inf;
}

int dfs(int now,int t,int limit)
{
    if (now==t || !limit) return limit;
    int flow=0,f;
    for (int i=cur[now]; i!=-1; i=edge[i].next)
    {
        cur[now]=i; int to=edge[i].to;
        if (deep[to]==deep[now]+1 && (f=dfs(to,t,min(edge[i].dis,limit))))
        {
            flow+=f;
            limit-=f;
            edge[i].dis-=f;
            edge[i^1].dis+=f;
            if (!limit) break;
        }
    }
    return flow;
}

void Dinic(int s,int t)
{
    while (bfs(s,t))
        maxflow+=dfs(s,t,inf);
}

void Tarjan(int x)
{
    dfn[x]=++cnt;
    low[x]=cnt;
    zhan[++top]=x;
    ifin[x]=true;
    for (int i=head[x]; i!=-1; i=edge[i].next)
    {
        int to=edge[i].to;
        if (!dfn[to])
        {
            Tarjan(to);
            low[x]=min(low[to],low[x]);
        }
        else if (ifin[to]) low[x]=min(low[x],dfn[to]);
    }
    if (dfn[x]==low[x])//找到一个强联通分量 
    {
        ifin[x]=false;
        col[x]=++num_col;
        while (zhan[top]!=x)
        {
            int now=zhan[top--]; 
            ifin[now]=false;
            col[now]=num_col;
        }
        top--;
    }
}

int tmp[maxn];
void BuildNewGraph()
{
    memset(head,-1,sizeof(head));
    for (int i=0; i<=num_edge; i++) tmp[i]=edge[i].dis;
    int tot=num_edge; num_edge=-1;
    for (int i=0; i<=tot; i++)
        if (tmp[i])
        {
            add_edge(edge[i^1].to,edge[i].to,1,edge[i].id);
        }
}

int main()
{
    memset(head,-1,sizeof(head));
    n=read(); m=read(); num=read();
    for (int i=1; i<=num; i++)
    {
        int x=read(); int y=read();
        add(x,y+n,1,i);
    }
    int S=0,T=n+m+1;
    for (int i=1; i<=n; i++) add(S,i,1,num+i);
    for (int i=1; i<=m; i++) add(i+n,T,1,num+n+i);
    Dinic(S,T);
    if (maxflow==num) {
   
   printf("0"); return 0;}

    BuildNewGraph();
    for (int i=1; i<=n+m; i++)
        if (!dfn[i]) Tarjan(i);
    int ans=0;
    for (int i=0; i<num; i++)
        if (col[edge[i].from]!=col[edge[i].to] && edge[i].from<edge[i].to) ans++;
    printf("%d\n",ans);
    for (int i=0; i<num; i++)
        if (col[edge[i].from]!=col[edge[i].to] && edge[i].from<edge[i].to) 
            printf("%d ",edge[i].id);
    return 0;
}

如果泥萌不喜欢我也可以贴一下std的代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int inf = 0x3fffffff, u = 40010, w = 300010;
int head[u], ver[w], edge[w], Next[w], d[u], e[w], c[u], sta[u], ins[u], dfn[u], low[u];
int n, m, p, s, t, i, j, tot, maxflow, ans, x, y, scc, st, num;
char str[10];
vector<int> a[u];
queue<int> q;

void add(int x, int y, int z) {
    ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
    ver[++tot] = x, edge[tot] = 0, Next[tot] = head[y], head[y] = tot;
}

bool bfs() {
    memset(d, 0, sizeof(d));
    while (q.size()) q.pop();
    q.push(s); d[s] = 1;
    while (q.size()) {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = Next[i])
            if (edge[i] && !d[ver[i]]) {
                q.push(ver[i]);
                d[ver[i]] = d[x] + 1;
                if (ver[i] == t) return 1;
            }
    }
    return 0;
}

int dinic(int x, int flow) {
    if (x == t) return flow;
    int rest = flow, k;
    for (int i = head[x]; i && rest; i = Next[i])
        if (edge[i] && d[ver[i]] == d[x] + 1) {
            k = dinic(ver[i], min(rest, edge[i]));
            if (!k) d[ver[i]] = 0;
            edge[i] -= k;
            edge[i ^ 1] += k;
            rest -= k;
        }
    return flow - rest;
}

void add2(int x, int y)
{
    a[x].push_back(y);
}

void tarjan(int x)
{
    dfn[x] = ++num; low[x] = num;
    sta[++st] = x; ins[x] = 1;
    int y;
    for (int i = 0; i<a[x].size(); i++)
        if (!dfn[y = a[x][i]])
        {
            tarjan(y);
            low[x] = min(low[x], low[y]);
        }
        else if (ins[y]) low[x] = min(low[x], dfn[y]);
        if (dfn[x] == low[x])
        {
            scc++;
            do { y = sta[st]; st--; ins[y] = 0; c[y] = scc; } while (x != y);
        }
}

int main()
{
    while (cin >> n >> m >> p)
    {
        memset(head, 0, sizeof(head));
        s = 0, t = n + m + 1; tot = 1; maxflow = 0;
        for (i = 1; i <= n; i++) add(s, i, 1);
        for (i = 1; i <= m; i++) add(i + n, t, 1);
        for (i = 1; i <= p; i++)
        {
            scanf("%d%d", &x, &y);
            add(x, n + y, 1), e[i] = tot;
        }
        while (bfs())
            while (i = dinic(s, inf)) maxflow += i;
        for (i = s; i <= t; i++) a[i].clear();
        for (i = 1; i <= p; i++)
            if (!edge[e[i]]) add2(ver[e[i]], ver[e[i] ^ 1]);
            else add2(ver[e[i] ^ 1], ver[e[i]]);
            for (i = 1; i <= n; i++)
                if (!edge[2 * i]) add2(i, s); else add2(s, i);
            for (i = 1; i <= m; i++)
                if (!edge[2 * (n + i)]) add2(t, n + i); else add2(n + i, t);
            memset(dfn, 0, sizeof(dfn));
            memset(ins, 0, sizeof(ins));
            memset(c, 0, sizeof(c));
            st = num = scc = ans = 0;
            for (i = s; i <= t; i++)
                if (!dfn[i]) tarjan(i);
            for (i = 1; i <= p; i++)
                if (edge[e[i]] || c[ver[e[i]]] == c[ver[e[i] ^ 1]]) ans++;
            cout << (ans = p - ans) << endl;
            if (!ans) cout << endl;
            for (i = 1; i <= p; i++)
                if (!edge[e[i]] && c[ver[e[i]]] != c[ver[e[i] ^ 1]])
                    if (--ans) printf("%d ", i); else printf("%d\n", i);
    }
}

总结

这可是说是我在了解思路之后自己写的代码吧;感觉这样会使自己理解的更深刻;

猜你喜欢

转载自blog.csdn.net/A_Comme_Amour/article/details/79693931