bzoj3876
有上下界的费用流
建图:每条边连接两个节点,边的费用为花费时间,下界为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; }