HDU 4067 Random Maze 费用流 构造欧拉通路

题目链接:点击打开链接

题意:

给定n个点m条有向边的图, 起点s ,终点t

下面m条边 u,v, a,b  若选择这条边花费为a, 不选择花费为b

构造一条欧拉通路使得起点是s,终点是t,且花费最小。

思路:

首先能想到一个简单的思路:假设所有边都选择,然后在费用流里跑,就会出现负环的问题。。

所以为了避免负环,让所有费用都为正值:

int sum = 0;

若a>b, 则 费用要为正只能是 a-b, 默认这条边是删除是, sum += b, 那么如果我们要选择这条边,则从u=>v, 费用为a-b, 流量为1即可

若a<b, 则费用要为正只能是b-a, 默认这条边是选择是, sum += a, 那么如果我们要删除这条边,则相当于选一条反向的边,即v=>u,费用为b-a, 流量为1

因为我们要保持流量统一,所以用炒鸡汇源来维持,



#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <iostream>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
using namespace std;
typedef int ll;
#define inf 0x3f3f3f3f  
#define N 300
#define M 605*605*4  
struct Edge {
	ll to, cap, cost, nex;
	Edge(){}
	Edge(ll to, ll cap, ll cost, ll next) :to(to), cap(cap), cost(cost), nex(next){}
} edge[M << 1];
ll head[N], edgenum;
ll D[N], A[N], P[N];
bool inq[N];
void add(ll from, ll to, ll cap, ll cost) {
	edge[edgenum] = Edge(to, cap, cost, head[from]);
	head[from] = edgenum++;
	edge[edgenum] = Edge(from, 0, -cost, head[to]);
	head[to] = edgenum++;
}
bool spfa(ll s, ll t, ll &flow, ll &cost) {
	for (ll i = 0; i <= t; i++) D[i] = inf;
	memset(inq, 0, sizeof inq);
	queue<ll>q;
	q.push(s);
	D[s] = 0; A[s] = inf;
	while (!q.empty()) {
		ll u = q.front(); q.pop();
		inq[u] = 0;
		for (ll i = head[u]; ~i; i = edge[i].nex)
		{
			Edge &e = edge[i];
			if (e.cap && D[e.to] > D[u] + e.cost)
			{
				D[e.to] = D[u] + e.cost;
				P[e.to] = i;
				A[e.to] = min(A[u], e.cap);
				if (!inq[e.to])
				{
					inq[e.to] = 1; q.push(e.to);
				}
			}
		}
	}
	//若费用为inf则中止费用流  
	if (D[t] == inf) return false;
	cost += D[t] * A[t];
	flow += A[t];
	ll u = t;
	while (u != s) {
		edge[P[u]].cap -= A[t];
		edge[P[u] ^ 1].cap += A[t];
		u = edge[P[u] ^ 1].to;
	}
	return true;
}
ll flow, cost;
ll Mincost(ll s, ll t){
	flow = 0, cost = 0;
	while (spfa(s, t, flow, cost));
	return cost;
}
void init(){ memset(head, -1, sizeof head); edgenum = 0; }

int n, m, s, t, from, to;
int in[N], out[N];
int main(){
	int T, Cas = 1; rd(T);
	while (T--){
		memset(in, 0, sizeof in);
		memset(out, 0, sizeof out);
		init();
		rd(n); rd(m); rd(s); rd(t);
		from = 0, to = n + 1;
		ll now = 0;
		for (int i = 0, u, v, a, b; i < m; i++){
			rd(u); rd(v); rd(a); rd(b);
			if (a > b){
				now += b;
				add(u, v, 1, a - b);
			}
			else {
				now += a;
				add(v, u, 1, b - a);
				in[v]++; out[u]++;
			}
		}
		in[s]++; out[t]++;
		for (int i = 1; i <= n; i++){
			if (in[i] > out[i])
				add(from, i, in[i] - out[i], 0);
			else if (in[i] < out[i])
				add(i, to, out[i] - in[i], 0);
		}
		Mincost(from, to);
		printf("Case %d: ", Cas++);
		for (int i = head[from]; ~i; i = edge[i].nex){
			if (edge[i].cap)flow = -1;
		}
		if (flow == -1)puts("impossible");
		else printf("%d\n", now + cost);		
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值