题目链接
带权有向图上的中国邮路问题:一名邮递员需要经过每条有向边至少一次,最后回到出发点,一条边多次经过权值要累加,问最小总权值是多少。
首先,每条边进过一次的话我们就先统计所有边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; }