洛谷 P2604 [ZJOI2010]网络扩容 解题报告
P2604 [ZJOI2010]网络扩容
题目描述
给定一张有向图,每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。求: 1、 在不扩容的情况下,1到N的最大流; 2、 将1到N的最大流增加K所需的最小扩容费用。
输入输出格式
输入格式:
输入文件的第一行包含三个整数N,M,K,表示有向图的点数、边数以及所需要增加的流量。 接下来的M行每行包含四个整数u,v,C,W,表示一条从u到v,容量为C,扩容费用为W的边。
输出格式:
输出文件一行包含两个整数,分别表示问题1和问题2的答案。
说明
30%的数据中,N<=100
100%的数据中,N<=1000,M<=5000,K<=10
k这么小,开始以为是分层图搞事呢。
第一问裸的最大流。
第二问我们对初始的每条边新建一条费用边,容量为inf,表示随便用,单位费用就连初始费用,表示要花钱。
保证增加的流量为k,我们搞一个超级源点容量为k连原源点1。
在跑完最大流之后的残余网络上跑最小费用最大流。
之前对最大流求法中的反悔理解的不深刻,在写这个题的时候一直觉得直接跑残量网络失去了对原来最大流的反悔机会。但实际上,基于路径交换的反悔原理,直接跑残量网络是没问题的。
代码里面偷了懒,万一有免费的边就错了
Code:
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=1010;
const int M=10010;
const int inf=0x3f3f3f3f;
int head[N],edge[M<<1],cost[M<<1],to[M<<1],Next[M<<1],cnt=1;
void add(int u,int v,int w,int c)
{
edge[++cnt]=w;cost[cnt]=c;Next[cnt]=head[u];to[cnt]=v;head[u]=cnt;
edge[++cnt]=0;cost[cnt]=-c;Next[cnt]=head[v];to[cnt]=u;head[v]=cnt;
}
int maxflow,n,m,k;
int dep[N];
bool bfs()
{
memset(dep,0,sizeof(dep));
dep[1]=1;
queue <int > q;
q.push(1);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=Next[i])
{
int v=to[i];
if(!cost[i]&&!dep[v]&&edge[i])
{
dep[v]=dep[u]+1;
if(v==n) return 1;
q.push(v);
}
}
}
return 0;
}
int dfs(int now,int flow)
{
if(now==n) return flow;
int rest=flow,k;
for(int i=head[now];i&&rest;i=Next[i])
{
int v=to[i];
if(!cost[i]&&edge[i]&&dep[v]==dep[now]+1)
{
k=dfs(v,min(rest,edge[i]));
if(!k) dep[v]=0;
rest-=k;
edge[i]-=k;
edge[i^1]+=k;
}
}
return flow-rest;
}
void Dinic()
{
int flow=0;
while(bfs())
while(flow=dfs(1,inf)) maxflow+=flow;
printf("%d ",maxflow);
}
int pre[N],used[N],dis[N];
int spfa()
{
queue <int > q;
q.push(0);
memset(dis,0x3f,sizeof(dis));
dis[0]=0;
while(!q.empty())
{
int u=q.front();
used[u]=0;
q.pop();
for(int i=head[u];i;i=Next[i])
{
int v=to[i];
if(edge[i]&&dis[v]>dis[u]+cost[i])
{
dis[v]=dis[u]+cost[i];
pre[v]=i;
if(!used[v])
used[v]=1,q.push(v);
}
}
}
return dis[n]!=inf;
}
void costflow()
{
int ans=0;
while(spfa())
{
int mi=inf;
for(int now=n;now;now=to[pre[now]^1])
mi=min(edge[pre[now]],mi);
ans+=dis[n]*mi;
for(int now=n;now;now=to[pre[now]^1])
edge[pre[now]]-=mi,edge[pre[now]^1]+=mi;
}
printf("%d\n",ans);
}
void init()
{
scanf("%d%d%d",&n,&m,&k);
int u,v,w,c;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&u,&v,&w,&c);
add(u,v,w,0),add(u,v,inf,c);
}
}
void work()
{
Dinic();
add(0,1,k,0);
costflow();
}
int main()
{
init();
work();
return 0;
}
2018.7.14