最小费用最大流
在这里学会的最小费用最大流问题。
定义:图G以S为源点,T为汇点。c(i, j)为G的容量,f(i, j)为G的流,w(i, j)为单位流量的费用且w(i, j) = -w(j, i)。费用wf = ∑(fij * wij) (i, j)∈E(G)。就是求最大流F的情况下保证wf最小。
思想:利用Ford-Fulkerson算法的思想,不断的在残留网络中找增广路,这里找的增广路是当前网络从S到T的以单位流量为权值的最短了。因为涉及道负权,所以可以选择spfa或者
bellman-ford。
实现代码:
const int N = 110; const int M = N*N*2; int n, m, k; int b[N], sumb; int c[N][N]; int S, T; struct node { int from, to, cost, flow, next; // } g[M]; int head[N], t; void init() { CL(head, -1); t = 0; } void add(int u, int v, int f, int w) { g[t].from = u; g[t].to = v; g[t].cost = w; g[t].flow = f; g[t].next = head[u]; head[u] = t++; g[t].from = v; g[t].to = u; g[t].cost = -w; g[t].flow = 0; g[t].next = head[v]; head[v] = t++; } void build() { init(); int i, j; S = 0; T = m + n + 1; for(i = 1; i <= n; ++i) { add(S, i, 1, 0); //flow, cost; } for(i = 1; i<= m; ++i) { for(j = 1; j <= n; ++j) { if(c[i][j]) add(j, i + n, 1, 0); } } for(j = 1; j <= m; ++j) { add(j+n, T, b[j]/k, k); if(b[j]%k > 1) { add(j + n, T, 1, b[j]%k); } } } int dis[N]; int pre[N]; bool vis[N]; queue<int> q; bool spfa() { //找最大费用 while(!q.empty()) q.pop(); CL(vis, 0); CL(dis, -1); CL(pre, -1); q.push(S); dis[S] = 0; vis[S] = true; pre[S] = -1; int u, v, w, i; while(!q.empty()) { u = q.front(); q.pop(); for(i = head[u]; i != -1; i = g[i].next) { v = g[i].to; w = g[i].cost; if(g[i].flow && dis[v] < dis[u] + w) { dis[v] = dis[u] + w; pre[v] = i; if(!vis[v]) { vis[v] = true; q.push(v); } } } vis[u] = false; } return dis[T] != -1; } int get_flow() { //增广路 int tmp = T; int res = inf; while(pre[tmp] != -1) { res = min(res, g[pre[tmp]].flow); tmp = g[pre[tmp]].from; } tmp = T; while(pre[tmp] != -1) { g[pre[tmp]].flow -= res; g[pre[tmp]^1].flow += res; tmp = g[pre[tmp]].from; } return res; } bool solve() { int Cost = 0, Flow = 0; //... while(spfa()) { Cost += dis[T]; Flow += get_flow(); } //printf("%d %d\n", Cost, Flow); //return Cost + n - Flow >= sumb; }
例题:poj2516
题解:http://www.cnblogs.com/vongang/archive/2012/04/14/2447566.html