网络流中的一种套路
近期竟然碰到了多次这种类型的题目,而且还变着法,还是总结一下为妙。
CodeForces 164C Machine Programming
以上三道题应该说是同一道题,建图方法都是焦作F那种。
$S$向1连容量为$k$的边以后,$l$向$r$连容量为1,费用为$-c$的边,然后$i$向$i+1(如果i > n,连向T)$连容量为$k$,费用为0的边。只有上界,无下界,就是你最多选$k$个,最少选0个。
Gym 101190D Delight for a Cat
这个题转换的好呀。竟然能转换成上面那种做题套路。
就是和上面不同的是 $i$向$i+1$连的边既有上界,又有下界。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1100; const ll INF = 1e18; ll dist[maxn]; int pv[maxn],pe[maxn]; struct edge { int to, cap, pre; int cost; }e[7000]; int tot = 0, head[maxn]; void init(int n) { tot = 0; fill(head, head + n + 1, -1); } void add(int from,int to,int cap, int cost) { e[tot].pre = head[from]; e[tot].to = to; e[tot].cap = cap; e[tot].cost = cost; head[from] = tot++; } void addedge(int from,int to,int cap, int cost) { add(from,to,cap,cost); add(to,from,0,-cost); } int vis[maxn]; void SPFA(int s, int t) { for(int i = 1; i < maxn; i++) dist[i] = INF; memset(vis, 0, sizeof(vis)); dist[s] = 0, vis[s] = 1; queue<int> q; q.push(s); while(!q.empty()) { int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u]; ~i; i = e[i].pre) { int to = e[i].to, cap = e[i].cap; if(cap > 0 && (dist[to] - (dist[u] + e[i].cost)) > 0) { pv[to] = u, pe[to] = i; dist[to] = dist[u] + e[i].cost; // printf("%d %d %d %lld\n",u, to, cap, dist[to]); if(!vis[to]) { vis[to] = 1; q.push(to); } } } //system("pause"); } } ll min_cost_flow(int s,int t,int f,int& max_flow) { ll ret = 0; while(f>0) { SPFA(s, t); if(dist[t] == INF) return ret;///同一目的地,每次增广路都是最小费用 ///当所有边的流量都流净后,即没有残余网络,返回。 int d = f; for(int v=t;v!=s;v=pv[v]) { d = min(d, e[pe[v]].cap); } f -= d; max_flow += d; ret += (ll)d*dist[t]; ///走一单位就消耗dist[t] for(int v=t;v!=s;v=pv[v]) { e[pe[v]].cap -= d; e[pe[v]^1].cap += d; } } return ret; } int ss[maxn], ee[maxn]; int main() { freopen("delight.in", "r", stdin); freopen("delight.out", "w", stdout); int n, k, ms, me; scanf("%d %d %d %d", &n, &k, &ms, &me); int S0 = n + 1, S = S0 + 1, T = S + 1; int mine = me, maxe = k - ms; init(n + 3); ll sum = 0; for(int i = 1; i <= n; i++) scanf("%d", &ss[i]), sum += ss[i]; for(int i = 1; i <= n; i++) scanf("%d", &ee[i]), ee[i] -= ss[i]; for(int i = 1; i <= n; i++) { if(i + k <= n) addedge(i, i + k, 1, -ee[i]); else addedge(i, T, 1, -ee[i]); } for(int i = 1; i <= n; i++) { if(i < n) addedge(i, i + 1, maxe - mine, 0); else addedge(i, T, maxe - mine, 0); } addedge(S, S0, maxe, 0); for(int i = 1; i <= k; i++) { addedge(S0, i, 1e9, 0); } int maxflow = 0; sum -= min_cost_flow(S, T, 1e9, maxflow); printf("%lld\n", sum); for(int i = 0; i < 2 * n; i += 2) { if(e[i].cap == 0) printf("E"); else printf("S"); } printf("\n"); return 0; }