poj 2135 Farm Tour

// 题意:有n个顶点和若干条无向边,边权值表示耗时多少,
// 要从顶点1走到n,再回到1,不能走重复路,求最短耗时
// 思路:对于题目给定的那些边,注意是无向边,每条边的容量为1,费用为每条边花费的时间。
// 加一个超级源点st和汇点ed,源点st与顶点 1 连一条容量为2,费用为0的边,
// 汇点ed与顶点 n 也连一条容量为2,费用为0的边. 接下来就是最小费用最大流问题了
// 因为是无向边,所以加入一条边(u,v)同时也得加入反向边(v,u), 故必须得用邻接表

#include <iostream> // 最小费用最大流,基于邻接表
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define INF 1<<30
const int MAXN = 1010;
const int MAXM = 40100;
int n, m, st, ed; //st和ed分别是超级源点和汇点
int d[MAXN], pos[MAXN]; //pos[to]记录边[from,to]的下标
int first[MAXN]; //first[s]保存结点s的第一条边的编号
int Next[MAXM]; //Next[e]表示编号为e的边的“下一条边”的编号
struct EdgeNode
{
int s,t; //起点和终点
int cap,cost,flow; //容量,费用和流量
}Edge[MAXM];
EdgeNode* u;
void addEdge(int from,int to,int cap,int cost)
{
//正向边
u=&Edge[m];
u->s=from;
u->t=to;
u->cap=cap;
u->cost=cost;
u->flow=0;
Next[m]=first[from];
first[from]=m;
m++;

//逆向边
swap(from,to);
u=&Edge[m];
u->s=from;
u->t=to;
u->cap=0;
u->cost=-cost;
u->flow=0;
Next[m]=first[from];
first[from]=m;
m++;

}
void build()
{
int e;
scanf("%d%d", &n, &e);
memset(first,-1,sizeof(first));
m=0; //边数下标从0开始
int u, v, c;
while(e--)
{
scanf("%d%d%d", &u, &v,&c);
addEdge(u,v,1,c); // 无向边
addEdge(v,u,1,c);
}
st=0; //超级源点
addEdge(st,1,2,0);
ed=n+1; //超级汇点
addEdge(n,ed,2,0);
}
void ek()
{
int f = 0, c = 0; //f,c分别保存最大流流量和该流量下的最小费用

queue<int> q;
int inq[MAXN];
while(1)
{
for(int i = 0; i <= n+1; i++) //结点下标从0到n+1
d[i] = (i==st ? 0 : INF);
memset(inq, 0, sizeof(inq));
memset(pos,-1,sizeof(pos));
q.push(st);
inq[st]=1;
int from,to;
while(!q.empty())
{
from = q.front(); q.pop();
inq[from] = 0;
for(int e = first[from]; e != -1; e = Next[e])
{
u=&Edge[e];
to=u->t;
if( u->cap > u->flow && d[to] > d[from]+u->cost )
{
d[to]=d[from]+u->cost;
pos[to]=e; //pos[to]记录边[from,to]的下标
if(!inq[to])
{
inq[to]=1;
q.push(to);
}
}
}
}
if(d[ed] == INF) break; //汇点不可达,表明当前流已经是最小费用最大流
int a = INF;
for(int e = pos[ed]; e != -1; e = pos[Edge[e].s])
{
a=min(a,Edge[e].cap-Edge[e].flow); //计算可改进量
}
for(int e = pos[ed]; e != -1; e = pos[Edge[e].s]) //增广
{
Edge[e].flow += a; //分别更新正向和逆向流量
Edge[e^1].flow -= a;
//因为正向与逆向边的下标是成对出现的:(0,1) (2,3)...所以e^1刚好是一对中的另一个
}
c += d[ed]*a; //更新总费用和流量
//f += a;
}
printf("%d\n", c);
}
int main()
{
build();
ek(); //源点0,汇点n+1
return 0;
}

posted on 2012-03-31 15:08  sysu_mjc  阅读(340)  评论(0编辑  收藏  举报

导航