1. 程式人生 > 實用技巧 >P2604 [ZJOI2010]網路擴容 費用流

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;
}