P2604 [ZJOI2010]网络扩容 费用流
P2604 [ZJOI2010]网络扩容(https://www.luogu.com.cn/problem/P2604)
题目描述
给定一张有向图,每条边都有一个容量 cc 和一个扩容费用 ww。这里扩容费用是指将容量扩大 11 所需的费用。求:
在不扩容的情况下,1 到 n 的最大流;
将 1 到 n 的最大流增加 k 所需的最小扩容费用。
输入格式
第一行包含三个整数 n,m,k,表示有向图的点数、边数以及所需要增加的流量。
接下来的 M 行每行包含四个整数 u,v,c,w,表示一条从u 到 v,容量为 c,扩容费用为 w 的边。
输出格式
输出文件一行包含两个整数,分别表示问题 1 和问题 2 的答案。
输入输出样例
输入
5 8 2
1 2 5 8
2 5 9 9
5 1 6 2
5 1 1 8
1 2 8 7
2 5 4 9
1 2 1 1
1 4 2 1
输出
13 19
说明/提示
数据规模与约定
对于 30% 的数据,保证 n≤100。
对于 100% 的数据,保证 1≤n≤10^3, 1≤m≤5×10^3,1≤k≤10,1≤u,v≤n。
思路
第一问裸的最大流。
第二问,我们在题目中的图,每条边u-v建立一条流量为INF,费用为w的边。用来扩容。
新建一个原点0,0-n建一条流量为k的边。跑最小费用最大流就可以了。
#include<bits/stdc++.h>
#define re register
using namespace std;
const int maxn = 1000 + 10;
const int maxm=1e6+10;
const int inf=1<<30;
const int INF = 0x3f3f3f3f;
struct po {
int to,dis,nxt,w;
} edge[maxm<<1];
struct max_folw {
int dis[maxn];
int vis[maxn];
int head[maxn];
int n,s,t,cut=-1;
int ans=0, fy=0;
void init(int N, int S, int T) {
ans=0, fy=0;
cut=-1;
n=N, s=S, t=T;
memset(head, -1, sizeof(head));
}
void add_edge(int from,int to,int w,int dis) {
edge[++cut].nxt=head[from];
edge[cut].to=to;
edge[cut].w=w;
edge[cut].dis=dis;
head[from]=cut;
}
void add(int from,int to,int w,int dis) {
add_edge(from,to,w,dis);
add_edge(to,from,0,-dis);
}
bool spfa() {
memset(vis,0,sizeof(vis));
for(re int i=0; i<=n; i++)
dis[i]=inf;
dis[t]=0;
vis[t]=1;
deque<int> q;
q.push_back(t);
while(!q.empty()) {
int u=q.front();
vis[u]=0;
q.pop_front();
for(re int i=head[u]; i!=-1; i=edge[i].nxt) {
int v=edge[i].to;
if(edge[i^1].w>0&&dis[v]>dis[u]-edge[i].dis) {
dis[v]=dis[u]-edge[i].dis;
if(!vis[v]) {
vis[v]=1;
if(!q.empty()&&dis[v]<dis[q.front()])
q.push_front(v);
else
q.push_back(v);
}
}
}
}
return dis[s]<inf;
}
int dfs(int u,int low) {
if(u==t) {
vis[t]=1;
return low;
}
int diss=0;
vis[u]=1;
for(re int i=head[u]; i!=-1; i=edge[i].nxt) {
int v=edge[i].to;
if(!vis[v]&&edge[i].w!=0&&dis[u]-edge[i].dis==dis[v]) {
int check=dfs(v,min(edge[i].w,low));
if(check>0) {
fy+=check*edge[i].dis;
edge[i].w-=check;
edge[i^1].w+=check;
low-=check;
diss+=check;
if(low==0)
break;
}
}
}
return diss;
}
void max_flow() {
while(spfa()) { //流量+k
vis[t]=1;
while(vis[t]) {
memset(vis,0,sizeof(vis));
ans+=dfs(s,inf);
}
}
}
} flow;
int x[5005], y[5005], z[5005], w[5005];
int main() {
int n, m, k; scanf("%d%d%d", &n, &m, &k);
flow.init(n+5, 1, n);
for(int i=1; i<=m; i++){
scanf("%d%d%d%d", &x[i], &y[i], &z[i], &w[i]);
flow.add(x[i], y[i], z[i], 0);
}
flow.max_flow();
int mx=flow.ans;
printf("%d ", mx);
flow.init(n+5, 0, n);
for(int i=1; i<=m; i++){
flow.add(x[i], y[i], z[i], 0);
flow.add(x[i], y[i], inf, w[i]);
}
flow.add(0, 1, k+mx, 0);
flow.max_flow();
printf("%d\n", flow.fy);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)