网络流模板(自用)

洛谷3381,最小费用最大流模板题

输入格式:

第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来M行每行包含四个正整数ui、vi、wi、fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi。

输出格式:

一行,包含两个整数,依次为最大流量和在最大流量情况下的最小费用。

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
const int maxn=5005;
const int maxm=50005;
int n,m,s,t;   //点的个数,边的个数,源点,汇点
int num=-1,tot,sum; //tot是最大流  sum是在最大流条件下的最小费用
int dis[maxn],b[maxn],xb[maxn],flow[maxn],fa[maxn];
int head[maxm];
struct Edge
{
    int from,to,dis,nxt,w;
}edge[maxm*2];//要建立反向边,所以这里要*2

void add_edge(int from,int to,int w,int dis)
{
    edge[++num].nxt=head[from];
    edge[num].from=from;
    edge[num].to=to;
    edge[num].w=w;
    edge[num].dis=dis;
    head[from]=num;
}


/*void add(int from,int to,int w,int dis)
{
    add_edge(from,to,w,dis);
    add_edge(to,from,0,-dis);
}*/


bool spfa()
{
    for(int i=0;i<=maxn;i++) //初始化1-n的dis数组
        dis[i]=inf;
    memset(b,0,sizeof(b));
    queue<int>que;
    while(!que.empty()) que.pop();
    memset(fa,-1,sizeof(fa));
    b[s]=1;
    dis[s]=0;
    fa[s]=0;
    flow[s]=inf;
    que.push(s);
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        b[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(edge[i].w>0&&dis[v]>dis[u]+edge[i].dis)
            {
                dis[v]=dis[u]+edge[i].dis;
                fa[v]=u;
                xb[v]=i;
                flow[v]=min(flow[u],edge[i].w);
                if(!b[v])
                {
                   b[v]=1;
                   que.push(v);
                }
            }
        }
    }
    return dis[t]<inf;
}

void max_flow()
{
   while(spfa())
   {
       int k=t;
       while(k!=s)
       {
           edge[xb[k]].w-=flow[t];
           edge[xb[k]^1].w+=flow[t];
           k=fa[k];
       }
       tot+=flow[t];
       sum+=flow[t]*dis[t];
   }
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d %d %d %d",&n,&m,&s,&t);
    for(int i=1;i<=m;i++)
    {
        int a,b,c,d;
        scanf("%d %d %d %d",&a,&b,&c,&d);//有向边起点,终点,边的流量,单位流量的费用
        add(a,b,c,d);
    }
    max_flow();
    cout<<tot<<" "<<sum<<endl; //tot是最大流,sum是在最大流条件下的最小费用*/
}

例:

CCPC-Wannafly Summer Camp #1——A——Birthday(最小费用最大流)

题意:把n个蛋糕放到m个区域里,每个蛋糕都有两种区域可以放,总花费为所有区域蛋糕数量的平方和,要求花费尽量小。

解法:想到把每个区域都拆成n个点,如果某一蛋糕可以放到一个区域里,就把蛋糕和这对应的点都连边,流量为1,连第i个点的代价是f(i)-f(i-1),f(x)=x*x。然后建立超级源点和超级汇点,源点与所有蛋糕连边且流量为1,费用为0,汇点与所有区域连边且流量为1,费用为0。跑一遍最小费用最大流,最后得出的费用即是ans。

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
const int maxn=5005;
const int maxm=50005;
int s,t;   //点的个数,边的个数,源点,汇点
int num=-1,tot,sum; //tot是最大流  sum是在最大流条件下的最小费用
int dis[maxn],b[maxn],xb[maxn],flow[maxn],fa[maxn];
int head[maxm];
struct Edge
{
    int from,to,dis,nxt,w;
}edge[maxm*2];//要建立反向边,所以这里要*2

void add_edge(int from,int to,int w,int dis)
{
    edge[++num].nxt=head[from];
    edge[num].from=from;
    edge[num].to=to;
    edge[num].w=w;
    edge[num].dis=dis;
    head[from]=num;
}


/*void add(int from,int to,int w,int dis)
{
    add_edge(from,to,w,dis);
    add_edge(to,from,0,-dis);
}*/


bool spfa()
{
    for(int i=0;i<=maxn;i++) //初始化1-n的dis数组
        dis[i]=inf;
    memset(b,0,sizeof(b));
    queue<int>que;
    while(!que.empty()) que.pop();
    memset(fa,-1,sizeof(fa));
    b[s]=1;
    dis[s]=0;
    fa[s]=0;
    flow[s]=inf;
    que.push(s);
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        b[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(edge[i].w>0&&dis[v]>dis[u]+edge[i].dis)
            {
                dis[v]=dis[u]+edge[i].dis;
                fa[v]=u;
                xb[v]=i;
                flow[v]=min(flow[u],edge[i].w);
                if(!b[v])
                {
                   b[v]=1;
                   que.push(v);
                }
            }
        }
    }
    return dis[t]<inf;
}

void max_flow()
{
   while(spfa())
   {
       int k=t;
       while(k!=s)
       {
           edge[xb[k]].w-=flow[t];
           edge[xb[k]^1].w+=flow[t];
           k=fa[k];
       }
       tot+=flow[t];
       sum+=flow[t]*dis[t];
   }
}

int main()
{
    memset(head,-1,sizeof(head));
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int a,b;
        cin>>a>>b;
        for(int j=1;j<=n;j++)
        {
            add_edge(i,n+(j-1)*m+a,1,2*j-1);
            add_edge(n+(j-1)*m+a,i,0,1-2*j);
            add_edge(i,n+(j-1)*m+b,1,2*j-1);
            add_edge(n+(j-1)*m+b,i,0,1-2*j);
        }
    }
    for(int i=1;i<=n;i++)
    {
        add_edge(0,i,1,0);
        add_edge(i,0,0,0);
    }
    for(int i=n+1;i<=n+n*m;i++)
    {
        add_edge(i,n+n*m+1,1,0);
        add_edge(n+n*m+1,i,0,0);
    }
    s=0;
    t=n+n*m+1;
    max_flow();
    cout<<sum<<endl;
}

洛谷3376 最大流模板题

输入格式:

第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来M行每行包含三个正整数ui、vi、wi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi)

输出格式:

一行,包含一个正整数,即为该网络的最大流。

EK算法目前在算法竞赛中几乎废弃,这里我们使用弧优化的dinic算法已经非常快了,虽然最大流的最快算法可能还是IASP,但是一般不会出现只能IASP过而卡dinic的算法的。

#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
const int maxm=100005;
const int inf=1e9;
int cnt=-1,n,m,s,t;  //点和边的个数,起点和终点的编号
int head[maxn],dep[maxn],cur[maxn];
struct Edge
{
    int next=-1;
    int to,w;
} edge[2*maxm]; //next初始值是-1,有反向边所以开了2倍数组
void add_edge(int u,int v,int w)
{
    edge[++cnt].next=head[u];
    edge[cnt].to=v;
    edge[cnt].w=w;
    head[u]=cnt;
}
bool bfs()
{
    queue<int>que;
    while(!que.empty()) que.pop();
    memset(dep,0,sizeof(dep));
    dep[s]=1;
    que.push(s);
    while(!que.empty())
    {
        int u=que.front();
        que.pop();
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].to;
            if((dep[v]==0)&&edge[i].w>0)
            {
                dep[v]=dep[u]+1;
                que.push(v);
            }
        }
    }
    if(dep[t]>0)
        return 1;
    return 0;
}
int dfs(int u,int flow)
{
    if(u==t) return flow;
    for(int &i=cur[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].to;
        if ((dep[v]==dep[u]+1)&&(edge[i].w!=0))
        {
            int d=dfs(v,min(flow,edge[i].w));
            if (d>0)
            {
                edge[i].w-=d;
                edge[i^1].w+=d;
                return d;
            }
        }
    }
    return 0;
}
int Dinic()
{
    int ans=0;
    while (bfs())
    {
        for (int i=1; i<=n; i++) 
            cur[i]=head[i];
        while (int d=dfs(s,inf))
        {
            ans+=d;
        }
    }
    return ans;
}
int main()
{
    memset(head,-1,sizeof(head));
    cin>>n>>m>>s>>t; //输入信息可能会随题目改变而不一样,请注意随机应变
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add_edge(a,b,c);
        add_edge(b,a,0);//反向边流量是0,切不可记作-c。
    }
    cout<<Dinic()<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zero___zero/article/details/82956498
今日推荐