带权有向图上的中国邮路问题:一名邮递员需要经过每条有向边至少一次,最后回到出发点,一条边多次经过权值要累加,问最小总权值是多少。
首先,每条边进过一次的话我们就先统计所有边sum值,然后就是判断哪些边重复走了。
统计所有点的入度与出度,设i点入度与出度的差为di;
如果di>0,addedge(s,i,di,0),即要走多di次去覆盖
如果di<0,addedge(i,t,|di|,0),同理
然后就是对于原来的边addedge(i,j,INF,cij)
这样跑一次最小费用最大流,然后费用加上sum 就是 所求;
注意的是,要图要连通,入度和出度>0
#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 10000;
const int MAXM = 10000;
const int INF = 0x3f3f3f3f;
struct Edge{
int to,next,cap,flow,cost;
}edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;
void init(int n){
N = n;
tol = 0;
memset(head,-1,sizeof(head));
}
void addedge(int u, int v, int cap, int cost){
edge[tol].to = v;
edge[tol].cap = cap;
edge[tol].cost = cost;
edge[tol].flow = 0;
edge[tol].next = head[u];
head[u] = tol++;
edge[tol].to = u;
edge[tol].cap = 0;
edge[tol].cost = - cost;
edge[tol].flow = 0;
edge[tol].next = head[v];
head[v] = tol++;
}
bool spfa(int s,int t){
queue<int> q;
for(int i = 0; i < N; i++){
dis[i] = INF;
vis[i] = false;
pre[i] = -1;
}
dis[s] = 0;
vis[s] = true;
q.push(s);
while(!q.empty()){
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && dis[v] > dis[u]+edge[i].cost){
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if(!vis[v]){
vis[v] = true;
q.push(v);
}
}
}
}
if(pre[t] == -1)return false;
return true;
}
int mincost(int s, int t, int &cost){
int flow = 0;
cost = 0;
while(spfa(s,t)){
int Min = INF;
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]){
if(Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
}
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]){
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost*Min;
}
flow += Min;
}
return flow;
}
int in[MAXN],out[MAXN],viss[MAXN];
void dfs(int u){
viss[u] = 1;
for(int i = head[u]; ~i; i = edge[i].next){
if(!viss[edge[i].to]){
dfs(edge[i].to);
}
}
}
int main(){
//freopen("in.txt","r",stdin);
int st, en, sum, u, v, c, n, m;
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
init(n+3);
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(viss,0,sizeof(vis));
st = 0;
en = n+1;
sum = 0;
for(int i = 1; i <= m; i++){
scanf("%d%d%d",&u,&v,&c);
u++,v++;
addedge(u,v,INF,c);
sum+=c;
in[v]++,out[u]++;
}
dfs(1);
bool flag = false;
for(int i = 1; i <= n; i++){
if(in[i] == 0 || out[i] == 0 || !viss[i]){
flag = true;
break;
}
if(in[i] > out[i]){
addedge(st,i,in[i]-out[i],0);
}
else if(in[i] < out[i]){
addedge(i,en,out[i]-in[i],0);
}
}
if(flag){
puts("-1");
continue;
}
mincost(st,en,c);
printf("%d\n",c+sum);
}
return 0;
}