最小费用最大流
2010的国赛题,题意很易懂不解释了。
分析部分参考了别人博客:一般的最小费用最大流是给出每个单位流量的费用,计算费用的时候是单位费用*流量,但是这里是单位费用*流量*流量,并不能单纯地计算,要巧妙地拆容量,每条边的原容量为cap,拆成cap条,每条容量都是1,第一次取这条路时是单位费用a,第二次是3*a,依次为5*a,7*a,9*a,这样你会发现,这条路走容量2的话刚好是4a,3为9a,4为16a,5为25a,接下来做一最小费用最大流即可
另外注意一点,题中说明边数最大为5000,而每条边的容量最大为5,所以在开数组的时候应该开到 2*5*50000,原来的一条边要拆成cap条边,所以边数变为cap倍,即*5,因为邻接表建图,有向边还要保存它的反边,每条边再*2,一共是*10
整道题就是拆边巧妙,其余部分都是最小费用最大流的模板
#include <cstdio> #include <cstring> #include <vector> #include <queue> #include <algorithm> using namespace std; #define INF 0x3f3f3f3f #define MAXN 110 #define MAXM 5010 //有向图 int nume; struct edge { int u,v,cap,cost,flow,next; }e[10*MAXM]; int first[MAXN]; int d[MAXN]; //spfa最短路 int p[MAXN]; //增广记录路径 int N,M,K,S,T; void add(int u, int v ,int cost ,int cap) { e[nume].u=u; e[nume].v=v; e[nume].cost=cost; e[nume].cap=cap; e[nume].flow=0; e[nume].next=first[u]; first[u]=nume++; } void build() { memset(first,-1,sizeof(first)); nume=0; S=0; T=N+1; //限制最大流为K add(S,1,0,K); add(1,S,0,0); add(N,T,0,K); add(T,N,0,0); for(int i=0; i<M; i++) { int u,v,cost,cap; scanf("%d%d%d%d",&u,&v,&cost,&cap); for(int j=1; j<=cap; j++) //拆边的方法 {//每条边的容量为1,单位费用为(2*j-1)*cost,其实就是1,3,5,7,9 add(u,v,(2*j-1)*cost,1); add(v,u,-(2*j-1)*cost,0); } } } bool spfa() { queue<int>q; bool inq[MAXN]; memset(inq,false,sizeof(inq)); memset(d,0x3f,sizeof(d)); memset(p,-1,sizeof(p)); d[S]=0; q.push(S); inq[S]=true; while(!q.empty()) { int u,v,cost,cap,flow; u=q.front(); q.pop(); inq[u]=false; for(int k=first[u]; k!=-1; k=e[k].next) { v=e[k].v; cost=e[k].cost; cap=e[k].cap; flow=e[k].flow; if(cap>flow && d[u]+cost < d[v]) { d[v]=d[u]+cost; p[v]=k; if(!inq[v]) { q.push(v); inq[v]=true; } } } } if(d[T]!=INF) return true; return false; } void mincost_maxflow() { int FLOW=0,COST=0; while(spfa()) { int min=INF; for(int k=p[T]; k!=-1; k=p[e[k].u]) if(e[k].cap-e[k].flow<min) min=e[k].cap-e[k].flow; //printf("min=%d\n",min); for(int k=p[T]; k!=-1; k=p[e[k].u]) //增广 { e[k].flow += min; e[k^1].flow -= min; } COST += d[T]*min; FLOW += min; } if(FLOW<K) printf("-1\n"); else printf("%d\n",COST); } int main() { while(scanf("%d%d%d",&N,&M,&K)!=EOF) { build(); mincost_maxflow(); } return 0; }