洛谷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;
}