您的位置:首页 > 其它

HOJ 2739 The Chinese Postman Problem

2013-10-06 19:32 453 查看
传说中的带权有向图上的中国邮路问题。

【题目大意】
带权有向图上的中国邮路问题:一名邮递员需要经过每条有向边至少一次,最后回到出发点,一条边多次经过权值要累加,问最小总权值是多少。(2 <= N <= 100, 1 <= M <= 2000)
【建模方法】
若原图的基图不连通,或者存在某个点的入度或出度为0则无解。统计所有点的入度出度之差Di,对于Di > 0的点,加边(s, i, Di, 0);对于Di < 0的点,加边(i, t, -Di, 0);对原图中的每条边(i, j),在网络中加边(i, j, ∞, Dij),其中Dij为边(i, j)的权值。求一次最小费用流,费用加上原图所有边权和即为结果。
若进一步要求输出最小权值回路,则对所有流量fij > 0的边(i, j),在原图中复制fij份,这样原图便成为欧拉图,求一次欧拉回路即可。

以上摘自Edelweiss《网络流建模汇总》

启示:

这篇论文的前面还讲到一个混合图欧拉回路的问题(就是给出一张既含有有向边又含有无向边的图,问你是否能通过确定图中一些无向边的方向使得图中存在欧拉回路),这种涉及到回路的问题,都要抓住每个点的出度和入度这两个非常重要的条件来构图。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <cstdlib>
#define maxn 110
#define maxm 5000
#define INF 1<<30
using namespace std;

struct MCMF{
int src,sink,e,n;
int first[maxn];
int cap[maxm],cost[maxm],u[maxm],v[maxm],next[maxm];
bool flag;
void init(){
e = 0;
memset(first,-1,sizeof(first));
}

void add_edge(int a,int b,int cc,int ww){
//printf("add:%d to %d,cap = %d,cost = %d\n",a,b,cc,ww);
u[e] = a;
cap[e] = cc;cost[e] = ww;v[e] = b;
next[e] = first[a];first[a] = e++;
u[e] = b;
cap[e] = 0;cost[e] = -ww;v[e] = a;
next[e] = first[b];first[b] = e++;
}

int d[maxn],pre[maxn],pos[maxn];
bool vis[maxn];

bool spfa(int s,int t){
memset(pre,-1,sizeof(pre));
memset(vis,0,sizeof(vis));
queue<int> Q;
for(int i = 0;i <= n;i++)   d[i] = INF;
Q.push(s);pre[s] = s;d[s] = 0;vis[s] = 1;
while(!Q.empty()){
int u = Q.front();Q.pop();
vis[u] = 0;
for(int i = first[u];i != -1;i = next[i]){
if(cap[i] > 0 && d[u] + cost[i] < d[v[i]]){
d[v[i]] = d[u] + cost[i];
pre[v[i]] = u;pos[v[i]] = i;
if(!vis[v[i]])  vis[v[i]] = 1,Q.push(v[i]);
}
}
}
return pre[t] != -1;
}

int Mincost;
int Maxflow;

int MinCostFlow(int s,int t,int nn){
Mincost = 0,Maxflow = 0,n = nn;
while(spfa(s,t)){
int min_f = INF;
for(int i = t;i != s;i = pre[i])
if(cap[pos[i]] < min_f) min_f = cap[pos[i]];
Mincost += d[t] * min_f;
Maxflow += min_f;
for(int i = t;i != s;i = pre[i]){
cap[pos[i]] -= min_f;
cap[pos[i]^1] += min_f;
}
}
return Mincost;
}
};

MCMF g;
int in[maxn],out[maxn];
bool G[maxn][maxn];
int N,M;

bool judge(){
int i,j,k;
for(i = 1;i <= N;i++)   G[i][i] = 1;
for(k = 1;k <= N;k++)
for(i = 1;i <= N;i++)
for(j = 1;j <= N;j++)
if(G[i][k] && G[k][j])  G[i][j] = 1;
for(i = 0;i < g.e;i += 2){
int a = g.u[i],b = g.v[i];
if(!(G[1][a] && G[b][1]))   return false;
}
return true;
}

int main(){
int kase;
scanf("%d",&kase);
while(kase--){
g.init();
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(G,0,sizeof(G));
scanf("%d%d",&N,&M);
int S = 0,T = N+1;
int ans = 0;
for(int i = 0;i < M;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
a++;b++;
G[a][b] = 1;
out[a]++;in[b]++;
g.add_edge(a,b,INF,c);
ans += c;
}
if(!judge()){
printf("-1\n");
continue;
}
int sum = 0;
for(int i = 1;i <= N;i++){
int d = in[i] - out[i];
if(d > 0)   g.add_edge(S,i,d,0);
else        g.add_edge(i,T,-d,0);
sum += abs(d);
}
ans += g.MinCostFlow(S,T,T);

printf("%d\n",ans);
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: