1. 程式人生 > >bzoj3876

bzoj3876

splay eof nbsp karp spf logs memset amp bool

有上下界的費用流

建圖:每條邊連接兩個節點,邊的費用為花費時間,下界為1,上界正無窮,源點為1,所有點連向1,下界0,上界正無窮,表示隨時可以返回,於是我們想求一個最小費用可行流

具體做法是先建立超級源匯,對於每條有上下界的邊(u,v),u->t,流量為u的入度,費用為0,s->v,流量為下界1,費用為(u,v)費用,u->v連邊,流量inf,花費為(u,v)的花費,這樣強迫流量流入流出,流滿了下界

其他邊和之前一樣連

u->t的目的是讓流入u的流量至少流過到v的邊的下界,這樣不會讓流到u的流量小於下界,s->v的邊的目的是強制流入v點至少下界的流量,花費為原來邊的花費,這樣保證了對於一條邊(u,v),流入u的流量不少於下界,從v流出的流量至少為下界,且花費為(u,v)的花費。保證對於點u,流入u的流量至少為所有u的出邊的下界之和,這樣流入u,也就是流入u出邊的下界可以被滿足,然後點v滿足了到v邊下界的流量肯定能流入v,且花費分別為所有流入v的邊

技術分享
#include<bits/stdc++.h>
using namespace std;
const int N = 10010, inf = 1 << 29;
struct edge {
    int nxt, to, f, c;
} e[N << 2];
int n, cnt = 1, source, sink;
int head[N], d[N], pree[N], prev[N], vis[N];
inline void link(int u, int v, int f, int c)
{
    e[++cnt].nxt = head[u];
    head[u] 
= cnt; e[cnt].to = v; e[cnt].f = f; e[cnt].c = c; } inline void insert(int u, int v, int f, int c) { link(u, v, f, c); link(v, u, 0, -c); } bool spfa() { queue<int> q; memset(d, -1, sizeof(d)); q.push(source); d[source] = 0; while(!q.empty()) {
int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u]; i; i = e[i].nxt) if(e[i].f && (d[e[i].to] > d[u] + e[i].c || d[e[i].to] == -1)) { d[e[i].to] = d[u] + e[i].c; pree[e[i].to] = i; prev[e[i].to] = u; if(vis[e[i].to] == 0) { vis[e[i].to] = 1; q.push(e[i].to); } } } return d[sink] != -1; } int Edmonds_Karp() { int ret = 0; while(spfa()) { int now = sink, delta = inf; while(now != source) { delta = min(delta, e[pree[now]].f); now = prev[now]; } now = sink; while(now != source) { e[pree[now]].f -= delta; e[pree[now] ^ 1].f += delta; now = prev[now]; } ret += d[sink] * delta; } return ret; } int main() { scanf("%d", &n); sink = n + 1; for(int i = 1; i <= n; ++i) { int m, b, t; scanf("%d", &m); insert(i, sink, m, 0); while(m--) { scanf("%d%d", &b, &t); insert(source, b, 1, t); insert(i, b, inf, t); } if(i != 1) insert(i, 1, inf, 0); } printf("%d\n", Edmonds_Karp()); return 0; }
View Code

bzoj3876