题解 P3381 【【模板】最小费用最大流】
Creeper_LKF
2017-07-18 16:16:57
朴素的EK+SPFA
```cpp
#include<bits/stdc++.h>
#define MAXN 5100
#define MAXM 100100
#define INF 1073741824
using namespace std;//INF不要开太大了否则可能会在一些操作下越界
struct Edge{
int from,to,re,c;//不记录流量和容量而直接记录残量可以节约空间方便操作(re=res,c=cost)
Edge(int _from,int _to,int _re,int _c) {from=_from,to=_to,re=_re,c=_c;}
Edge(){}
};
Edge table[MAXM+1];//一定要记得给逆向边开数组(因为这个为P3381贡献了N个TLE+RE)
int pre[MAXN+1],dis[MAXN+1];//前驱&&最短路(权值为cost)
int nxt[MAXM+1],fr[MAXM+1];//记录邻接表
int n,m,s,t,tot=1;
int max_f,min_c;//最大流最小费用
bool inq[MAXN+1];
deque<int> mession;//双向队列
void MCMF(){
while(true){
fill(dis+1,dis+n+1,INF);
memset(inq,false,sizeof(inq));
dis[s]=0;
mession.push_back(s);
inq[s]=true,dis[s]=0;
int now;//现在点
while(!mession.empty()){
now=mession.front(),mession.pop_front();
inq[now]=false;
for(int i=fr[now];i!=-1;i=nxt[i]){
Edge &tar=table[i];//目标点
if(tar.re>0&&dis[tar.to]>dis[now]+tar.c){
dis[tar.to]=dis[now]+tar.c;
pre[tar.to]=i;
if(!inq[tar.to]){
if(mession.empty()||dis[tar.to]<dis[mession.front()]) mession.push_front(tar.to);//优先处理的优化(要加入空队列的判断,否则会RE)
else mession.push_back(tar.to);
inq[tar.to]=true;//是否在队列中
}
}
}
}
if(dis[t]==INF) return;//集中的EK写法(只有一个函数)(此时不可增广)
int ret=INF;
for(int i=t;i!=s;i=table[pre[i]].from)
if(ret>table[pre[i]].re)
ret=table[pre[i]].re;//获取最小的可增广流量
for(int i=t;i!=s;i=table[pre[i]].from) table[pre[i]].re-=ret,table[pre[i]^1].re+=ret;//增广
max_f+=ret,min_c+=ret*dis[t];
}
}
inline int read(){//不用考虑负数的读入优化
int num=0;
char c;
while(isspace(c=getchar()));
while((num=num*10+c-48)&&isdigit(c=getchar()));
return num;
}
int main(){
n=read(),m=read(),s=read(),t=read();
memset(nxt,-1,sizeof(nxt));//注意结束的标记
for(int i=1;i<=m;i++){
int x=read(),y=read(),c1=read(),c2=read();
table[++tot]=Edge(x,y,c1,c2);
nxt[tot]=fr[x],fr[x]=tot;
table[++tot]=Edge(y,x,0,-c2);//方便直接调用正向边逆向边
nxt[tot]=fr[y],fr[y]=tot;
}
MCMF();
printf("%d %d",max_f,min_c);
return 0;
}
```