[CQOI2016]不同的最小割

题目

【问题描述】

  学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割。对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割。

  而对冲刺NOI竞赛的选手而言,求带权图中两点的最小割已经不是什么难事了。我们可以把视野放宽,考虑有N个点的无向连通图中所有点对的最小割的容量,共能得到N(N−1)/2个数值。这些数值中互不相同的有多少个呢?这似乎是个有趣的问题。

【输入格式】

  输入文件第一行包含两个数N,M,表示点数和边数。
  接下来M行,每行三个数u,v,w,表示点u和点v(从1开始标号)之间有条边权值是w。

【输出格式】

  输出文件第一行为一个整数,表示个数。

【输入样例】

4 4
1 2 3
1 3 6
2 4 5
3 4 4

【输出样例】

3

【数据范围】

对于 50% 的数据,N ≤ 200,M ≤ 2000
对于 100% 的数据,1 ≤ N ≤ 850,1 ≤ M ≤ 8500,1 ≤ w ≤ 100000

分析

结论:一张图最多n-1个最小割,所以就是分治啦。

详细证明及更多知识请自行查阅:最小割树

之后就是自然而然的分治了,我们分治只是为了找出所有不同的最小割,因此每一次跑网络流依旧要跑完整张图,但是要用染色来标记当前正在分治的那些点。

最多有n个割,时间复杂度也就是n*网络流算法

技巧

对于无向图的网络流中,可以用两条有向边加,并且最大流量都为cap,就不用连接四条边了,效率大概翻倍。
正确性伪证明:假设从u->v,流量为flow,则反向边为-flow,和反向边流量限制是0还是cap没有关系,反之亦然。

代码

算法流程:
任选两个点,跑一次最大流,然后找出最小割以及对应的ST集合
把S集合全部染成一个新的颜色,然后对S继续操作
同理对T继续操作
最后如果有一个染色的次数相同的话那么就停止。

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=855+100,maxm=8505+1000,inf=1e9;
int np,first[maxn];
int other(int p){
    return ((p+1)^1)-1;
}
struct edge{
    int to,next,cap,flow;
}E[maxm<<1];
void add(int u,int v,int c)
{
    E[++np]=(edge){v,first[u],c,0};
    first[u]=np;
    E[++np]=(edge){u,first[v],c,0};
    first[v]=np;
}
set<int>ans;
int n,m;
void Init()
{
    int u,v,a;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&a);
        add(u,v,a);
    }
}
int a[maxn],co[maxn];
int gap[maxn],dist[maxn];
int SAP(int i,int lim,int s,int t)
{
    if(i==t)return lim;
    int flow=0;
    for(int p=first[i];p;p=E[p].next)
    {
        int j=E[p].to;
        if(dist[j]+1==dist[i])
        {
            int tmp=SAP(j,min(lim-flow,E[p].cap-E[p].flow),s,t);
            flow+=tmp;
            E[p].flow+=tmp;
            E[other(p)].flow-=tmp;
            if(flow==lim || dist[s]>=n)return flow;
        }
    }
    if(flow==0)
    {
        if(--gap[dist[i]]==0)dist[s]=n;
        gap[++dist[i]]++;
    }
    return flow;
}
int maxflow(int s,int t)
{
    memset(gap,0,sizeof(gap));
    memset(dist,0,sizeof(dist));
    gap[0]=n;
    int flow=0;
    while(dist[s]return flow;
}
int cc=0;
queue<int>q;
void BFS(int s)
{
    memset(a,0,sizeof(a));a[s]=inf;
    while(!q.empty())q.pop();
    q.push(s);
    while(!q.empty())
    {
        int i=q.front();q.pop();
        for(int p=first[i];p;p=E[p].next)
        {
            int j=E[p].to;
            if(!a[j] && E[p].cap-E[p].flow)
            {
                a[j]=min(a[i],E[p].cap-E[p].flow);
                q.push(j);
            }
        }
    }
}
void dvd(int c,int s,int t)//当前颜色为s,起点为s,终点为t,当前有n个点 
{
    for(int p=1;p<=np;p++)E[p].flow=0;
    int tmp=maxflow(s,t);
    ans.insert(tmp);
    BFS(s);
    cc++;
    int s1=-1,s2=-1,t1=-1,t2=-1;
    for(int i=1;i<=n;i++)
    {
        if(co[i]==c)
        {
            if(a[i])
            {
                co[i]=cc;
                if(s1==-1)
                    s1=i;
                else if(t1==-1)
                    t1=i;
            }
            else
            {
                if(s2==-1)
                    s2=i;
                else if(t2==-1)
                    t2=i;
            }
        }
    }
    if(t1!=-1)
        dvd(cc,s1,t1);
    if(t2!=-1)
        dvd(c,s2,t2);
}
int main()
{
//  freopen("in.txt","r",stdin);
//  freopen("out1.txt","w",stdout);
    Init();
    dvd(0,1,2);
    cout<return 0; 
}

你可能感兴趣的:(网络流,图论,小思考)