zkw费用流

挂个链接:
zkw的zkw博客
原理参考zkw的博客

题目:
cf edu 109 D

两个版本:区别在最短路的求法上面
1.修改顶标(原始版本)
适用于:最终流量较大, 而费用取值范围不大的图, 或者是增广路径比较短的图 (如二分图),

#include<bits/stdc++.h>
#define For(aa, bb, cc) for(int aa = (bb); aa <= (int)(cc); ++aa)
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 5e3 + 10, maxm = 1e6 + 10;
int n, s, t, allflow, allcost;
int a[maxn];
int vis[maxn], cur[maxn], dis[maxn];
int be[maxn], ne[maxm], to[maxm], flow[maxm], cost[maxm], e;

void add_edge(int x, int y, int z, int w){
	to[++e] = y, ne[e] = be[x], be[x] = e, flow[e] = z, cost[e] = w;
	to[++e] = x, ne[e] = be[y], be[y] = e, flow[e] = 0, cost[e] = -w;
}

int dfs(int u, int f){
	vis[u] = 1;
	if(u == t) return allcost += dis[s] * f, allflow += f, f;
	int sum = 0;
	for(int &i = cur[u]; i; i = ne[i]){
		int v = to[i];
		if(!vis[v] && flow[i] > 0 && dis[v] == dis[u] - cost[i]){
			int tmp = dfs(v, min(flow[i], f));
			if(tmp){
				flow[i] -= tmp;
				flow[i ^ 1] += tmp;
				f -= tmp;
				sum += tmp;
			}
			if(!f) return sum;
		}
	}
	return sum;
}

bool spfa(){
	For(i, 1, n + 2) dis[i] = inf, vis[i] = 0;
	deque<int> q;
	dis[t] = 0;
	q.push_back(t);
	while(!q.empty()){
		int u = q.front();
		q.pop_front();
		vis[u] = 0;
		for(int i = be[u]; i; i = ne[i]){
			int v = to[i];
			if(flow[i ^ 1] > 0 && dis[v] > dis[u] - cost[i]){
				dis[v] = dis[u] - cost[i];
				if(!vis[v]){
					vis[v] = 1;
					if(!q.empty() && dis[v] < dis[q.front()]) q.push_front(v);
					else q.push_back(v);
				}
			}
		}
	}
	return dis[s] < inf;
}

void minflow(){
	allflow = allcost = 0;
	while(spfa()){
		memset(vis, 0, sizeof vis);
		memcpy(cur, be, sizeof be);
		dfs(s, inf);
	}
}
 
int main(){
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> n;
	For(i, 1, n) cin >> a[i];
	e = 1;
	s = n + 1, t = n + 2;
	For(i, 1, n){
		if(a[i]) add_edge(s, i, 1, 0);
		else add_edge(i, t, 1, 0);
	}
	For(i, 1, n - 1) add_edge(i, i + 1, inf, 1), add_edge(i + 1, i, inf, 1);
	minflow();
	printf("%d\n", allcost);
	return 0;
}

2.spfa的SLF优化(比较普适)

#include<bits/stdc++.h>
#define For(aa, bb, cc) for(int aa = (bb); aa <= (int)(cc); ++aa)
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 5e3 + 10, maxm = 1e6 + 10;
int n, s, t, allflow, allcost;
int a[maxn];
int vis[maxn], cur[maxn], dis[maxn];
int be[maxn], ne[maxm], to[maxm], flow[maxm], cost[maxm], e;

void add_edge(int x, int y, int z, int w){
	to[++e] = y, ne[e] = be[x], be[x] = e, flow[e] = z, cost[e] = w;
	to[++e] = x, ne[e] = be[y], be[y] = e, flow[e] = 0, cost[e] = -w;
}

int dfs(int u, int f){
	vis[u] = 1;
	if(u == t) return allcost += dis[s] * f, allflow += f, f;
	int sum = 0;
	for(int &i = cur[u]; i; i = ne[i]){
		int v = to[i];
		if(!vis[v] && flow[i] > 0 && dis[v] == dis[u] - cost[i]){
			int tmp = dfs(v, min(flow[i], f));
			if(tmp){
				flow[i] -= tmp;
				flow[i ^ 1] += tmp;
				f -= tmp;
				sum += tmp;
			}
			if(!f) return sum;
		}
	}
	return sum;
}

bool spfa(){
	For(i, 1, n + 2) dis[i] = inf, vis[i] = 0;
	deque<int> q;
	dis[t] = 0;
	q.push_back(t);
	while(!q.empty()){
		int u = q.front();
		q.pop_front();
		vis[u] = 0;
		for(int i = be[u]; i; i = ne[i]){
			int v = to[i];
			if(flow[i ^ 1] > 0 && dis[v] > dis[u] - cost[i]){
				dis[v] = dis[u] - cost[i];
				if(!vis[v]){
					vis[v] = 1;
					if(!q.empty() && dis[v] < dis[q.front()]) q.push_front(v);
					else q.push_back(v);
				}
			}
		}
	}
	return dis[s] < inf;
}

void minflow(){
	allflow = allcost = 0;
	while(spfa()){
		memset(vis, 0, sizeof vis);
		memcpy(cur, be, sizeof be);
		dfs(s, inf);
	}
}
 
int main(){
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cin >> n;
	For(i, 1, n) cin >> a[i];
	e = 1;
	s = n + 1, t = n + 2;
	For(i, 1, n){
		if(a[i]) add_edge(s, i, 1, 0);
		else add_edge(i, t, 1, 0);
	}
	For(i, 1, n - 1) add_edge(i, i + 1, inf, 1), add_edge(i + 1, i, inf, 1);
	minflow();
	printf("%d\n", allcost);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值