/* 题意:n个地点有m条路相连,从1走到n再回来,不走重复的路(这题居然认为,同一条路,按不同的方向走算是走不重复的路) 最小费用最大流 因为要来回,素以相当于找两条1~n的最短路 s~1 流量2 费用0 n~t 流量2 费用0 是每条无向边都拆分成两条有向边 把有向边分别加到图里(还有他的退边,反费用,流量0) 退边和他的反向边的表示是一样的,但是费用和流量是不一样的,所以不可以用邻接矩阵,只能用邻接表了 */ #include<iostream> #include<queue> using namespace std; const int inf = 0x7fffffff; const int en = 40010; const int vn = 1005; int n,m,mincost,s,t; int pre[vn],vis[vn],dist[vn];//求最短路径的时候用到的 pre保存流向他的那个边的编号 vis访问标志 dist距离 struct edge//边 { int u[en],v[en],f[en],c[en],head[en],yong,next[en];//边的前端和后端 流量 费用 邻接表的头 分配记录 下一条边 edge()//初始化 { clear(); } void clear()//清除 除 head外 其他的都是主动写入的故不用清除 { yong=1; memset(head,0,sizeof(head)); } void adde(int uu,int ww,int cost,int flow)//加一条边 加他自己和他的退边 { add(uu,ww,cost,flow); add(ww,uu,-cost,0); } void add(int uu,int ww,int cost,int flow) //加边 { u[yong]=uu; v[yong]=ww; f[yong]=flow; c[yong]=cost; next[yong]=head[uu]; head[uu]=yong; yong++; } }e; int min(int a,int b) { return a<b?a:b; } int spfa()//求最短路径 { int i,u,v,p; memset(vis,0,sizeof(vis)); memset(pre,0,sizeof(dist)); queue<int>q; q.push(s); vis[s]=1; for(i=1;i<=t;++i) dist[i]=inf; dist[s]=0; while(!q.empty()) { u=q.front(); for(p=e.head[u];p;p=e.next[p]) { v=e.v[p]; if(e.f[p]&&dist[v]>dist[u]+e.c[p]) { dist[v]=dist[u]+e.c[p]; pre[v]=p;//记录流向v的那条边的编号 方便后面压入流 if(!vis[v]) { q.push(v); vis[v]=1; } } } q.pop(); vis[u]=0; } if(dist[t]==inf) return 0; return 1; } void addf()//压入流 { int i,j; i=pre[t]; while(i!=0) { if(i%2)//求边i的退边 之前看别人的代码,直接用了 i^1 结果老是错 人家是从 0 开始用的 我是从1开始用的 所以这里处理一下 j=i+1; else j=i-1; e.f[i]--; e.f[j]++; mincost+=e.c[i]; i=pre[e.u[i]]; } return; } int main() { int i,u,v,c; cin>>n>>m; mincost=0; s=0; t=n+1; for(i=1;i<=m;++i) { cin>>u>>v>>c; e.adde(u,v,c,1); e.adde(v,u,c,1); } e.adde(s,1,0,2); e.adde(n,t,0,2); while(spfa()) addf(); cout<<mincost<<endl; return 0; }