1. 程式人生 > >P2053 [SCOI2007]修車 費用流

P2053 [SCOI2007]修車 費用流

bool bits pri span hang 題解 ffffff col fin

$ \color{#0066ff}{ 題目描述 }$

同一時刻有N位車主帶著他們的愛車來到了汽車維修中心。維修中心共有M位技術人員,不同的技術人員對不同的車進行維修所用的時間是不同的。現在需要安排這M位技術人員所維修的車及順序,使得顧客平均等待的時間最小。

說明:顧客的等待時間是指從他把車送至維修中心到維修完畢所用的時間。

\(\color{#0066ff}{輸入格式}\)

第一行有兩個數M,N,表示技術人員數與顧客數。

接下來n行,每行m個整數。第i+1行第j個數表示第j位技術人員維修第i輛車需要用的時間T。

\(\color{#0066ff}{輸出格式}\)

最小平均等待時間,答案精確到小數點後2位。

\(\color{#0066ff}{輸入樣例}\)

2 2
3 2
1 4

\(\color{#0066ff}{輸出樣例}\)

1.50

\(\color{#0066ff}{數據範圍與提示}\)

(2<=M<=9,1<=N<=60), (1<=T<=1000)

\(\color{#0066ff}{題解}\)

這種對應關系還有數據範圍,顯然就是費用流了

現在的問題是怎麽建邊

發現,每輛車的等待時間跟前面修車人所修的車有關

假設某人修了10輛車

那麽他修第一輛的時候,後面9人都等了這個時間,也就是貢獻+=9倍的這個時間

因此我們單獨考慮這個人修的每一輛車對時間的貢獻

把每個人都拆成n個點

對於一個人的第k個點,連向它的車代表他倒數第k次修它

也就是連T*k的邊權,倒數第k次修它,那麽後面k輛車就會產生這麽多貢獻

跑一遍費用流即可(zkw大法好)

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 1e5 + 10;
const int inf = 0x7fffffff;
struct node {
    int to, can, dis;
    node *nxt, *rev;
    node(int to = 0, int can = 0, int dis = 0, node *nxt = NULL): to(to), can(can), dis(dis), nxt(nxt) {
        rev = NULL;
    }
};
node *head[maxn], *cur[maxn];
bool vis[maxn];
int n, m, s, t;
int dis[maxn];
void add(int from, int to, int can, int dis) {
    head[from] = new node(to, can, dis, head[from]);
}
void link(int from, int to, int can, int dis) {
    add(from, to, can, dis);
    add(to, from, 0, -dis);
    head[from]->rev = head[to];
    head[to]->rev = head[from];
}
bool spfa() {
    std::queue<int> q;
    for(int i = s; i <= t; i++) dis[i] = inf, vis[i] = false;
    q.push(t);
    dis[t] = 0;
    while(!q.empty()) {
        int tp = q.front(); q.pop();
        vis[tp] = false;
        for(node *i = head[tp]; i; i = i->nxt) {
            if(dis[i->to] > dis[tp] - i->dis && i->rev->can) {
                dis[i->to] = dis[tp] - i->dis;
                if(!vis[i->to]) {
                    q.push(i->to);
                    vis[i->to] = true;
                }
            }
        }
    }
    return dis[s] != inf;
}
int dfs(int x, int change) {
    
    if(x == t || !change) return change;
    int flow = 0, ls;
    vis[x] = true;
    for(node *i = head[x]; i; i = i->nxt) {
        if(!vis[i->to] && dis[i->to] == dis[x] - i->dis && (ls = dfs(i->to, std::min(change, i->can)))) {
            flow += ls;
            change -= ls;
            i->can -= ls;
            i->rev->can += ls;
            if(!change) break;
        }
    }
    return flow;
}

int zkw() {
    int cost = 0;
    while(spfa()) {
        vis[t] = true;
        while(vis[t]) {
            for(int i = s; i <= t; i++) vis[i] = false;
            cost += dis[s] * dfs(s, inf);
        }
    }
    return cost;
}
int main() { 
    m = in(), n = in(), s = 0, t = m * n + n + 1;
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= n; j++)
            link(s, (i - 1) * n + j, 1, 0);
    for(int i = 1; i <= n; i++) link(m * n + i, t, 1, 0);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) {
            int x = in();
            for(int k = 1; k <= n; k++) link((j - 1) * n + k, n * m + i, 1, k * x);
        }
    printf("%.2f\n", (double)zkw() / n);
    return 0;
}

P2053 [SCOI2007]修車 費用流