POJ - 2135 Farm Tour (最小费用流,相当于从起点到终点两条不一样的路,求最短路。)

题目链接

题意:给你一副无向图,问从1->n->1这样走一个来回所用的最短路径是多少,每条边只能走一次

思路:我们知道了起点终点,既然每条路不能重复,那么我们就将每条路的流量设为一,这样每条路就只能走一次了。当然起点终点的时候我们要设成 2, 这样才能走两条不一样的路。把路的长度设为费用就可以了。

两个最小费用流的方法,

还有一个zkw 网络流。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define mem(x,v) memset(x,v,sizeof(x))
const int N = 5e3 + 10;
const int M = 5e4 + 10;
const int INF = 2147483647;
int n,m;
int cnt = -1;
struct node{
    int u,v,cost,next,w;
}f[M];
int head[N],pre[N],dist[N],flow[M];
bool que[N];

void Add(int u, int v, int w, int cost){
    cnt++;
    f[cnt].next = head[u];
    head[u] = cnt;
    f[cnt].u = u;
    f[cnt].v = v;
    f[cnt].w = w;
    f[cnt].cost = cost;
    return;
}
void Add_edge(int u,int v,int w, int cost){
    Add(u,v,w,cost);
    Add(v,u,0,-cost);
    return;
}

bool spfa(){
    mem(pre,-1);
    mem(que,0);
    mem(flow,0);
    mem(dist,127);
    queue<int>q;
    while(!q.empty()) q.pop();
    q.push(0);
    dist[0] = 0;
    flow[0] = INF;
    que[0] = 1;
    while(!q.empty()){
        int u = q.front(); q.pop();
        que[u] = 0;
        for (int i = head[u]; i != -1; i = f[i].next){
            int v = f[i].v;
            if ((f[i].w > 0) && (dist[u] + f[i].cost < dist[v])){
                dist[v] = dist[u] + f[i].cost;
                flow[v] = min(flow[u],f[i].w);
                pre[v] = i;
                if (que[v] == 0){
                    q.push(v);
                    que[v] = 1;
                }
            }
        }
    }
    if (pre[n+1] == -1) return 0;
    return 1;
}
void init(){
    scanf("%d%d",&n,&m);
    mem(head,-1);
    for (int i = 1; i <= m; i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        Add_edge(u,v,1,w);
        Add_edge(v,u,1,w);
    }
    Add_edge(0,1,2,0);
    Add_edge(n,n+1,2,0);
    int Ans = 0;
    while(spfa()){
        int now = n + 1;
        int last = pre[now];
        while(now != 0){
            f[last].w -= flow[n + 1];
            f[last ^ 1].w += flow[n + 1];
            now = f[last].u;
            last = pre[now];
        }
        Ans += dist[n + 1] * flow[n + 1];
    }
    cout<<Ans<<endl;
}

int main(){
    init();
    return 0;
}

ZKW 网络流,+ spfa 优化,

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define rep(i,a,b)  for (int i = a; i < b; i++)
#define per(i,a,b)  for (int i = a; i > b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 1e5+10;
const int M = 1e5+10;
bool vis[N];
int p[N],Next[N],Head[N],dis[N],c[N],w[N];
int n,m,s,t,cnt,ans = 0;
void _Add(int u, int v, int cc, int ww){
	cnt++;
	Next[cnt] = Head[u];
	Head[u] = cnt;
	p[cnt] = v;
	c[cnt] = cc;
	w[cnt] = ww;
	return;
}
void Add_edge(int u, int v, int c, int w){
	_Add(u,v,c,w);
	_Add(v,u,0,-w);
	return;
}
bool spfa(int s, int t){
	mem(vis,0);
	rep(i,0,n+1) dis[i] = INF;
	dis[t] = 0; vis[t] = 1;
	deque<int>q; q.push_back(t);
	while(! q.empty()){
		int now = q.front();
		q.pop_front();
		for (int i = Head[now]; i != -1; i = Next[i]){
			if (c[i^1] && dis[p[i]] > dis[now] - w[i]){
				dis[p[i]] = dis[now] - w[i];
				if (!vis[p[i]]){
					vis[p[i]] = 1;
					if (!q.empty() && dis[p[i]] < dis[q.front()]) q.push_front(p[i]); else
					q.push_back(p[i]);
				}
			}
		}
		vis[now] = 0;
	}
	return dis[s] < INF;
}
int dfs(int x, int low){
	if (x == t) {
		vis[t] = 1;
		return low;
	}
	int used = 0,d;
	vis[x] = 1;
	for(int k = Head[x]; k != -1; k = Next[k]){
		if (!vis[p[k]] && c[k] && dis[x] - w[k] == dis[p[k]]){
			d = dfs(p[k],min(c[k],low-used));
			if (d) {
				ans += d * w[k];
			    c[k] -= d;
			    c[k^1] += d;
			    used += d;
		    }
		    if (used == low) break;
		}
	}
	return used;
}
void costflow(){
	int flow = 0;
	while(spfa(s,t)){
		vis[t] = 1;
		while(vis[t]){
			mem(vis,0);
			flow += dfs(s,INF);
		}
	}
	return;
}
void init(){
	scanf("%d%d",&n,&m);
	mem(Head,-1);
	cnt = -1;
	rep(i,0,m){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		Add_edge(x,y,1,z);
		Add_edge(y,x,1,z);
	}
	s = 0; t = n+1;
	Add_edge(0,1,2,0);
	Add_edge(n,t,2,0);
	return; 
}
int main(){
	init();
	costflow();
	printf("%d\n",ans);
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值