hdu4009 Transfer water 最小树形图

  每一户人家水的来源有两种打井和从别家接水,每户人家都可能向外输送水。

  打井和接水两种的付出代价都接边。设一个超级源点,每家每户打井的代价就是从该点(0)到该户人家(1~n)的边的权值。接水有两种可能,从高处接水,那么代价是哈密顿距离与Y的乘积(可以认为就是水管的费用);从低处接水,还要加上付出水泵的钱(水管+水泵的费用)。这样就可以建图了。图论,会建图的话问题就解决一半了。

  然后,用模版来解题。不过朱刘算法的模版时间复杂度的差异还是蛮大的。我的模版的建图是邻接矩阵,时间复杂度是O(N^3)。超时,过不了。在网上找了一份按前向星(边的起点、终点、权值)建图的模版。AC了。该算法的复杂度是O(M)。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 using namespace std;
  5 const int N = 1010, M=1010000,INF=0x3f3f3f3f;
  6 int pre[N],id[N],in[N],vis[N];
  7 int tot;//边数
  8 struct node
  9 {
 10     int u,v,w;
 11 }e[M];
 12 void adde(int i,int j,int k)
 13 {
 14     e[tot].u=i;e[tot].v=j;e[tot++].w=k;
 15 }
 16 int zhuliu(int root ,int vn)
 17 {
 18     int ans=0;
 19     int cnt;
 20     while(1)
 21     {
 22         for(int i=0;i<vn;i++)
 23             in[i]=INF,id[i]=-1,vis[i]=-1;
 24         for(int i=0;i<tot;i++)
 25         {
 26             if(in[e[i].v]>e[i].w && e[i].u!=e[i].v)
 27             {
 28                 pre[e[i].v]=e[i].u;
 29                 in[e[i].v]=e[i].w;
 30             }
 31         }
 32         in[root]=0;
 33         pre[root]=root;
 34         for(int i=0;i<vn;i++)
 35         {
 36             ans+=in[i];
 37             if(in[i]==INF)//这一步可以看出是否存在这样一棵树形图,因此可以略去DFS
 38                 return -1;
 39         }
 40         cnt=0;
 41         for(int i=0;i<vn;i++)
 42         {
 43             if(vis[i]==-1)
 44             {
 45                 int t=i;
 46                 while(vis[t]==-1)
 47                 {
 48                     vis[t]=i;
 49                     t=pre[t];
 50                 }
 51                 if(vis[t]!=i || t==root) continue;
 52                 for(int j=pre[t];j!=t;j=pre[j])
 53                     id[j]=cnt;
 54                 id[t]=cnt++;
 55             }
 56         }
 57         if(cnt==0) break;
 58         for(int i=0;i<vn;i++)
 59             if(id[i]==-1)
 60                 id[i]=cnt++;
 61         for(int i=0;i<tot;i++)
 62         {
 63             int u,v;
 64             u=e[i].u;
 65             v=e[i].v;
 66             e[i].u=id[u];
 67             e[i].v=id[v];
 68             e[i].w-=in[v];
 69         }
 70         vn=cnt;
 71         root=id[root];
 72     }
 73     return ans;
 74 }
 75 
 76 struct Node
 77 {
 78     int a,b,c;
 79 }nd[N];
 80 int main()
 81 {
 82     //freopen("test.txt","r",stdin);
 83     int n,x,y,z,i,j,k,d;
 84     while(scanf("%d%d%d%d",&n,&x,&y,&z)!=EOF)
 85     {
 86         if(!n) break;
 87         tot=0;
 88         for(i=1;i<=n;i++)
 89         {
 90             scanf("%d%d%d",&nd[i].a,&nd[i].b,&nd[i].c);
 91             adde(0,i,nd[i].c*x);
 92         }
 93         for(i=1;i<=n;i++)
 94         {
 95             scanf("%d",&k);
 96             while(k--)
 97             {
 98                 scanf("%d",&j);
 99                 if(i==j) continue;
100                 d=abs(nd[i].a-nd[j].a)+abs(nd[i].b-nd[j].b)+abs(nd[i].c-nd[j].c);
101                 d*=y;
102                 if(nd[i].c<nd[j].c)  d+=z;
103                 adde(i,j,d);
104             }
105         }
106         int ans=zhuliu(0,n+1);
107         if(ans==-1) printf("poor XiaoA
");
108         else printf("%d
",ans);
109     }
110     return 0;
111 }
View Code

下面是邻接矩阵建图的模版,虽然超时了。但是模版还是有借鉴意义的。模版来自哈工大出版的《图论及应用》。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 using namespace std;
  5 const int N = 1010, INF=0x3f3f3f3f;
  6 int Map[N][N];
  7 bool vis[N],f[N];
  8 //f是缩点标记,1表示该点已经被缩掉
  9 int pre[N];
 10 int zhuliu(int n)
 11 {
 12     int sum=0;
 13     int i,j,k;
 14     for(i=0;i<n;i++)
 15     {
 16         f[i]=0;
 17         Map[i][i]=INF;
 18     }
 19     pre[0]=0;
 20     while(1)
 21     {
 22         //求最短弧集合E0
 23         for(i=1;i<n;i++)
 24         {
 25             if(f[i]) continue;
 26             pre[i]=i;
 27             for(j=0;j<n;j++)
 28                 if(!f[j]&&Map[j][i]<Map[pre[i]][i])  pre[i]=j;
 29             if(pre[i]==i) return -1; //没有入边,不存在最小树形图
 30         }
 31         //检查E0
 32         for(i=1;i<n;i++)
 33         {
 34             if(f[i]) continue ;
 35             //从当前点开始找环
 36             for(j=0;j<n;j++) vis[j]=0;
 37             vis[0]=1;
 38             j=i;
 39             do
 40             {
 41                 vis[j]=1;
 42                 j=pre[j];
 43             }
 44             while(!vis[j]) ;
 45             if(!j) continue; //没有找到环
 46             //收缩G中的有向环
 47             i=j;
 48             //将整个环的权值保存,累计入原图的最小树形图
 49             do
 50             {
 51                 sum+=Map[pre[j]][j];
 52                 j=pre[j];
 53             }
 54             while(j!=i) ;
 55             j=i;
 56             //对与环上的点有关的边,修改边权
 57             do
 58             {
 59                 for(k=0; k<n; k++)
 60                     if(!f[k]&&Map[k][j]<INF&&k!=pre[j])
 61                         Map[k][j]-= Map[pre[j]][j];
 62                 j=pre[j];
 63             }
 64             while(j!=i) ;
 65             //缩点,将整个环缩成i号点,所有与环上的点有关的边转移到点i
 66             for(j=0;j<n;j++)
 67             {
 68                 if(j==i) continue;
 69                 for(k=pre[i]; k!=i; k=pre[k])
 70                 {
 71                     if(Map[k][j]<Map[i][j]) Map[i][j] =Map[k][j];
 72                     if(Map[j][k]<Map[j][i]) Map[j][i] =Map[j][k];
 73                 }
 74             }
 75             //标记环上其他的点为被缩掉
 76             for(j=pre[i];j!=i;j=pre[j]) f[j] =1;
 77             //当前环缩点结束,形成新的图G’,跳出继续求G’的最小树形图
 78             break;
 79         }
 80         //如果所有的点都被检查且没有环存在,现在的最短弧集合E0就是
 81         //最小树形图,累计如sum, 算法结束
 82         if(i==n)
 83         {
 84             for(i=1;i<n;i++) if(!f[i]) sum+=Map[pre[i]][i];
 85             break;
 86         }
 87     }
 88     return sum;
 89 }
 90 
 91 struct node
 92 {
 93     int a,b,c;
 94 }nd[N];
 95 int main()
 96 {
 97     //freopen("test.txt","r",stdin);
 98     int n,x,y,z,i,j,k,d;
 99     while(scanf("%d%d%d%d",&n,&x,&y,&z)!=EOF)
100     {
101         if(!n) break;
102         for(i=0;i<=n;i++)
103             for(j=0;j<i;j++)
104                 Map[j][i]=Map[i][j]=INF;
105         for(i=1;i<=n;i++)
106         {
107             scanf("%d%d%d",&nd[i].a,&nd[i].b,&nd[i].c);
108             Map[0][i]=nd[i].c*x;
109         }
110         for(i=1;i<=n;i++)
111         {
112             scanf("%d",&k);
113             while(k--)
114             {
115                 scanf("%d",&j);
116                 if(i==j) continue;
117                 d=abs(nd[i].a-nd[j].a)+abs(nd[i].b-nd[j].b)+abs(nd[i].c-nd[j].c);
118                 d*=y;
119                 if(nd[i].c>=nd[j].c)  d+=z;
120                 Map[i][j]=min(d,Map[i][j]);
121             }
122         }
123         printf("%d
",zhuliu(n+1));
124     }
125     return 0;
126 }
View Code
原文地址:https://www.cnblogs.com/Potato-lover/p/3942321.html