hit2739

好题,回路的问题一般都要转化为度数来做
若原图的基图不连通,或者存在某个点的入度或出度为0则无解。
统计所有点的入度出度之差di
对于di>0的点,加边(s,i,di,0);
对于di<0的点,加边(i,t,-di,0);
对原图中的每条边(i,j),在网络中加边(i,j,inf,边权),
最小费用流的解加上原图所有边权和即为答案。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5
  6 using namespace std;
  7 struct way{int po,next,flow,cost;} e[40010];
  8 const int inf=100000007;
  9 int pre[110],p[110],cur[110],d[110],fa[110],cd[110],rd[110],q[2000010];
 10 bool v[110];
 11 int n,m,len,t;
 12
 13 int getf(int x)
 14 {
 15     if (fa[x]!=x) fa[x]=getf(fa[x]);
 16     return fa[x];
 17 }
 18
 19 bool check()
 20 {
 21     for (int i=1; i<=n; i++)
 22         if (!rd[i]||!cd[i]||getf(i)!=getf(1)) return 0;
 23     return 1;
 24 }
 25
 26 void add(int x,int y,int f,int c)
 27 {
 28      e[++len].po=y;
 29      e[len].flow=f;
 30      e[len].cost=c;
 31      e[len].next=p[x];
 32      p[x]=len;
 33 }
 34
 35 void build(int x,int y, int f, int c)
 36 {
 37      add(x,y,f,c);
 38      add(y,x,0,-c);
 39 }
 40
 41 bool spfa()
 42 {
 43      int f=1,r=1;
 44      for (int i=1; i<=t; i++) d[i]=inf;
 45      memset(v,false,sizeof(v));
 46      d[0]=0; q[1]=0;
 47      while (f<=r)
 48      {
 49            int x=q[f++];
 50            v[x]=0;
 51            for (int i=p[x]; i!=-1; i=e[i].next)
 52            {
 53                int y=e[i].po;
 54                if (e[i].flow&&d[x]+e[i].cost<d[y])
 55                {
 56                   d[y]=d[x]+e[i].cost;
 57                   pre[y]=x; cur[y]=i;
 58                   if (!v[y])
 59                   {
 60                      q[++r]=y;
 61                      v[y]=1;
 62                   }
 63                }
 64            }
 65      }
 66      return d[n]<inf;
 67 }
 68
 69 int mincost()
 70 {
 71     int j,s=0;
 72     while (spfa())
 73     {
 74           int neck=inf;
 75           for (int i=t; i; i=pre[i])
 76           {
 77               j=cur[i];
 78               neck=min(neck,e[j].flow);
 79           }
 80           s+=d[t]*neck;
 81           for (int i=t; i; i=pre[i])
 82           {
 83               j=cur[i];
 84               e[j].flow-=neck;
 85               e[j^1].flow+=neck;
 86           }
 87     }
 88     return s;
 89 }
 90
 91 int main()
 92 {
 93     int cas;
 94     scanf("%d",&cas);
 95     while (cas--)
 96     {
 97         scanf("%d%d",&n,&m);
 98         memset(p,255,sizeof(p)); len=-1;
 99         memset(rd,0,sizeof(rd));
100         memset(cd,0,sizeof(cd));
101         for (int i=1; i<=n; i++) fa[i]=i;
102         int ans=0;
103         for (int i=1; i<=m; i++)
104         {
105             int x,y,u,v,z;
106             scanf("%d%d%d",&x,&y,&z);
107             cd[++x]++;rd[++y]++;
108             build(x,y,inf,z);
109             u=getf(x),v=getf(y);
110             if (u!=v) fa[u]=v;
111             ans+=z;
112         }
113         if (!check())
114         {
115             puts("-1");
116             continue;
117         }
118         t=n+1;
119         for (int i=1; i<=n; i++)
120             if (rd[i]>cd[i]) build(0,i,rd[i]-cd[i],0);
121             else build(i,t,cd[i]-rd[i],0);
122         ans+=mincost();
123         printf("%d\n",ans);
124     }
125 }

时间: 2024-10-11 17:20:07

hit2739的相关文章

HIT2739 The Chinese Postman Problem(最小费用最大流)

题目大概说给一张有向图,要从0点出发返回0点且每条边至少都要走过一次,求走的最短路程. 经典的CPP问题,解法就是加边构造出欧拉回路,一个有向图存在欧拉回路的充分必要条件是基图连通且所有点入度等于出度. 而这题,果断联想到混合图欧拉回路的做法,用最小费用最大流解决: 先只考虑所有边都只走一次,计算出各个点的出度和入度,出度不等于入度的点就需要选择几条边去改变调整它们 对于出度多的就和容量网络的汇点连容量出度-入度费用0的边,入度多的源点就向其同样地连边 对于原图中的所有边<u,v>由u向v连容