[网络流24题(4/24)] 运输问题(最小费用最大流)
题意:
有\(m\)个仓库和\(n\)个零售商,第\(i\)个仓库送到第\(j\)个零售商需要花费\(v[i][j]\)元。现在需要让仓库的供给量以及零售商的收获量相同,问最小花费以及最大花费。
分析:
相当经典的最小费用最大流的模型。因为要保证供给以及收获相同,即代表着流量平衡,因此我们可以让超级源点\(sp\)跟对应的仓库连一条流量为\(a_i\),费用为\(0\)的边,同时让对应的零售商跟超级汇点\(ep\)连一条流量为\(b_i\),费用为\(0\)的边。而对于仓库与零售商,我们只需要将仓库和零售商之间连一条流量为无穷,费用为\(v[i][j]\)的边。
对于最小花费问题,我们只需要在上述的图中跑最小费用最大流即可。
而对于最大花费的问题,我们只需要将上述的图中的仓库与零售商的改为连一条流量为无穷,费用为\(-v[i][j]\)的边,最后在新图上跑一边最小费用最大流,而最后最小费用的相反数即是答案。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int maxm = 20005;
int head[maxn],cnt=0;
int dis[maxn],vis[maxn],sp,ep,maxflow,cost;
const int INF=0x3f3f3f3f;
struct Node{
int to,next,val,cost;
}q[maxm<<1];
void init(){
memset(head,-1,sizeof(head));
cnt=2;
maxflow=cost=0;
}
void addedge(int from,int to,int val,int cost){
q[cnt].to=to;
q[cnt].next=head[from];
q[cnt].val=val;
q[cnt].cost=cost;
head[from]=cnt++;
}
void add_edge(int from,int to,int val,int cost){
addedge(from,to,val,cost);
addedge(to,from,0,-cost);
}
bool spfa(){
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
dis[sp]=0;
vis[sp]=1;
queue<int>que;
que.push(sp);
while(!que.empty()) {
int x = que.front();
que.pop();
vis[x]=0;
for(int i=head[x];i!=-1;i=q[i].next){
int to=q[i].to;
if(dis[to]>dis[x]+q[i].cost&&q[i].val){
dis[to]=dis[x]+q[i].cost;
if(!vis[to]){
que.push(to);
vis[to]=1;
}
}
}
}
return dis[ep]!=0x3f3f3f3f;
}
int dfs(int x,int flow){
if(x==ep){
vis[ep]=1;
maxflow+=flow;
return flow;
}
int used=0;
vis[x]=1;
for(int i=head[x];i!=-1;i=q[i].next){
int to=q[i].to;
if((vis[to]==0||to==ep)&&q[i].val!=0&&dis[to]==dis[x]+q[i].cost){
int minflow=dfs(to,min(flow-used,q[i].val));
if(minflow!=0){
cost+=q[i].cost*minflow;
q[i].val-=minflow;
q[i^1].val+=minflow;
used+=minflow;
}
if(used==flow) break;
}
}
return used;
}
int mincostmaxflow(){
while(spfa()){
vis[ep]=1;
while(vis[ep]){
memset(vis,0,sizeof(vis));
dfs(sp,INF);
}
}
return maxflow;
}
int a[maxn],b[maxn],v[maxn][maxn];
int main()
{
int n,m;
init();
scanf("%d%d",&n,&m);
sp=n+m+1,ep=n+m+2;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
add_edge(sp,i,a[i],0);
}
for(int i=1;i<=m;i++){
scanf("%d",&b[i]);
add_edge(i+n,ep,b[i],0);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&v[i][j]);
add_edge(i,j+n,INF,v[i][j]);
}
}
mincostmaxflow();
printf("%d\n",cost);
init();
for(int i=1;i<=n;i++){
add_edge(sp,i,a[i],0);
}
for(int i=1;i<=m;i++){
add_edge(i+n,ep,b[i],0);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
add_edge(i,j+n,INF,-v[i][j]);
}
}
mincostmaxflow();
printf("%d\n",-cost);
return 0;
}