本来的思路是纯纯地打一个大暴力
在残余网络上跑spfa,每跑出一条增广路就是当前能扩容的最小花费
然后k<=10,只需要跑最多十次:)
正解是建平行边啦,容量为inf,费用为扩容费用,起到的效果是等价的~
#include<bits/stdc++.h> using namespace std; const int maxn=100010; const int inf=INT_MAX; bool vis[maxn]; int n,m,c,w,k,s,t,x,y,z,f,dis[maxn],pre[maxn],last[maxn],flow[maxn],maxflow,mincost; //dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量 struct Edge{ int to,next,flow,dis;//flow流量 dis花费 }edge[maxn]; int head[maxn],num_edge; queue <int> q; void add_edge(int from,int to,int flow,int dis) { edge[++num_edge].next=head[from]; edge[num_edge].to=to; edge[num_edge].flow=flow; edge[num_edge].dis=dis; head[from]=num_edge; } bool spfa(int s,int t) { memset(dis,0x7f,sizeof(dis)); memset(flow,0x7f,sizeof(flow)); memset(vis,0,sizeof(vis)); q.push(s); vis[s]=1; dis[s]=0; pre[t]=-1; while (!q.empty()) { int now=q.front(); q.pop(); vis[now]=0; for (int i=head[now]; i!=-1; i=edge[i].next) { if (edge[i].flow>0 && dis[edge[i].to]>dis[now]+edge[i].dis)//正边 { dis[edge[i].to]=dis[now]+edge[i].dis; pre[edge[i].to]=now; last[edge[i].to]=i; flow[edge[i].to]=min(flow[now],edge[i].flow);// if (!vis[edge[i].to]) { vis[edge[i].to]=1; q.push(edge[i].to); } } } } return pre[t]!=-1; } void MCMF() { while (spfa(s,t)) { int now=t; maxflow+=flow[t]; mincost+=flow[t]*dis[t]; while (now!=s) {//从源点一直回溯到汇点 edge[last[now]].flow-=flow[t];//flow和dis容易搞混 edge[last[now]^1].flow+=flow[t]; now=pre[now]; } } } int ix[maxn],iy[maxn],ic[maxn],iw[maxn]; int main() { //freopen("lys.in","r",stdin); memset(head,-1,sizeof(head));num_edge=-1;//初始化 cin>>n>>m>>k; for (int i=1; i<=m; i++) { cin>>x>>y>>c>>w; add_edge(x,y,c,0); add_edge(y,x,0,0); // u,v,flow,cost //反边的流量为0,花费是相反数 ix[i]=x;iy[i]=y;ic[i]=c;iw[i]=w; } s=1,t=n; MCMF(); cout<<maxflow<<" "; t=n+1; add_edge(n,t,k,0); add_edge(t,n,0,0); for(int i=1;i<=m;i++){ add_edge(ix[i],iy[i],inf,iw[i]); add_edge(iy[i],ix[i],0,-iw[i]); } MCMF(); cout<<mincost<<endl; return 0; }
标签:now,P2604,int,flow,ZJOI2010,edge,maxn,行边,dis From: https://www.cnblogs.com/liyishui2003/p/16814740.html