hdu4009 Transfer water 最小树形图

时间:2021-08-24 16:02:13

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

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

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

hdu4009 Transfer water 最小树形图hdu4009 Transfer water 最小树形图
  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\n");
108 else printf("%d\n",ans);
109 }
110 return 0;
111 }
View Code

 

 

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

hdu4009 Transfer water 最小树形图hdu4009 Transfer water 最小树形图
  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\n",zhuliu(n+1));
124 }
125 return 0;
126 }
View Code