题目描述

原题链接

一群小丑演员,以其出色的柔术表演,可以无限量的钻进同一辆汽车中,而闻名世界。

现在他们想要去公园玩耍,但是他们的经费非常紧缺。

他们将乘车前往公园,为了减少花费,他们决定选择一种合理的乘车方式,可以使得他们去往公园需要的所有汽车行驶的总公里数最少。

为此,他们愿意通过很多人挤在同一辆车的方式,来减少汽车行驶的总花销。

由此,他们可以很多人驾车到某一个兄弟的家里,然后所有人都钻进一辆车里,再继续前进。

公园的停车场能停放的车的数量有限,而且因为公园有入场费,所以一旦一辆车子进入到公园内,就必须停在那里,不能再去接其他人。

现在请你想出一种方法,可以使得他们全都到达公园的情况下,所有汽车行驶的总路程最少。

输入格式

第一行包含整数\(n\),表示人和人之间或人和公园之间的道路的总数量。

接下来\(n\)行,每行包含两个字符串\(A、B\)和一个整数\(L\),用以描述人A和人B之前存在道路,路长为\(L\),或者描述某人和公园之间存在道路,路长为\(L\)。

道路都是双向的,并且人数不超过\(20\),表示人的名字的字符串长度不超过\(10\),公园用“Park”表示。

再接下来一行,包含整数\(s\),表示公园的最大停车数量。

你可以假设每个人的家都有一条通往公园的道路。

输出格式

输出“Total miles driven: xxx”,其中xxx表示所有汽车行驶的总路程。

输入样例:

  1. 10
  2. Alphonzo Bernardo 32
  3. Alphonzo Park 57
  4. Alphonzo Eduardo 43
  5. Bernardo Park 19
  6. Bernardo Clemenzi 82
  7. Clemenzi Park 65
  8. Clemenzi Herb 90
  9. Clemenzi Eduardo 109
  10. Park Herb 24
  11. Herb Eduardo 79
  12. 3

输出样例:

  1. Total miles driven: 183

解题报告

题意理解

给定一张\(N\)个点,\(M\)个边的无向图,求出无向图的一颗最小生成树,但是我们要求一号节点的入度不可以超过给定的整数\(S\)

也就是一个最小生成树,要求它的一号节点,最多只能和S个节点相连.

思路确定

题意就已经告诉我们,我们的必备算法必然是最小生成树.但是具体的算法流程,我们还得思考一下.

首先,我们要知道两个性质.

  1. 一个最小生成树,它的任意一棵子树都是最小生成树.

  2. 也就是一个最小生成树,实际上是由很多棵最小生成树构成的.

根据上面所言的性质,我们可以这么考虑这道题目.


首先我们不妨不去考虑这个有特殊限制的一号节点,那么我们忽略掉这个一号节点后.

原图变成了\(T\)个连通块.

\[设T表示为抛去第一号节点后有T个连通块
\]

那么我们对于每一个连通块,都可以在这个连通块内部求出它的最小生成树.


我们接下来再来考虑,如何让这些连通块与我们的一号节点相连,构成我们题目的最小生成树.

首先我们很容易求出一个相对较小的生成树.切记不是最小生成树

对于每一个连通块而言,显然要在每一个连通块之中选出一个节点与我们的一号节点的权值最小.即\((1,p)\)的权值要尽量小.

\[p \in 每一个单独的连通块
\]

那么此时,我们发现我们成功将节点们连接在一起了,构成了一个最小生成树,那么现在的问题就是,如何优化我们的生成树,将其变成我们的最小生成树.

我们现在知道和一号节点连接的点,一共有\(T\)个,但是题目中要求不多于\(S\)个节点就好了.

分类讨论一下

\[若S<T
\]

那么我们必然就是无解情况.

\[若S=T
\]

那么此时我们的生成树,就是我们的最小生成树.

\[若S>T
\]

**我们发现,对于一个节点而言,它不一定要属于自己原本的连通块,它可以和节点\(1\)相连 **

我们可以考虑无向图从节点\(1\)出发的每条边\((1,x,z)\),其中边权为\(z\),那么假如说\((1,x)\)这条边,它不在当前的生成树内.

那么我们就可以找到从当前生成树总\((1,x)\)这条路径上的权值最大边\((u,v,w)\)将它删除.

如果说我添加\((1,x,z)\)这条边,我就可以删除\((u,v,w)\)这条边.

然后我们这颗生成树的权值就可以减少\(w-z\).

综上所述,我们每一次从每一个连通块里面找,找到让\(w-z\)的值最大的点,然后添加\((1,x,z)\),删除\((u,v,w)\)

直到

\[T==S 或者w-z \le 0
\]

也就是不可以加边,或者加边已经木有意义了.


代码解释

这道题目的思路,花了我十五分钟不到的时间理解,然后代码花费了两个晚上的时间,尤其是中途花了一个晚上的时间,打了一百五十多行代码,就在要打完的时候,电脑挂掉了,然后没有保存........................

痛不欲生的我,再次打了一遍.时间就这样多了一晚上

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N=1010;
  4. #define quick() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)//读入优化
  5. #define mem(a,b) memset(a,b,sizeof(a))//初始化
  6. #define init() mem(g,0),mem(vis,false),root=cnt=tot2=tot=0,q.clear(),q2.clear()//初始化大军
  7. #define add(a,b,c) g[a][b]=g[b][a]=c//无向图加边
  8. #define mk(a,b) make_pair(a,b)//简便
  9. int tot2,tot,n,m,fa[N],cnt,c,s,root,g[31][31],dis[31][31];
  10. //tot2为边的数量
  11. //tot为点的数量
  12. //cnt为编号的数量
  13. map<string,int> q;//映射关系
  14. map<pair<int,int>,bool> q2;//统计这条边是否出现在最小生成树中
  15. int vis[N];//标记连通块的编号
  16. string a,b;
  17. int find(int x)
  18. {
  19. return x==fa[x]?fa[x]:fa[x]=find(fa[x]);//并查集找红太阳
  20. }
  21. struct edge1
  22. {
  23. int x,y,w;
  24. } g2[N];
  25. int cmp (edge1 a,edge1 b)
  26. {
  27. return a.w<b.w;//最小边排序
  28. }
  29. struct node
  30. {
  31. int u,v,d;//(u,v)节点权值为d
  32. inline void inits()
  33. {
  34. u=v=0;
  35. d=-1;
  36. }
  37. } dp[N];
  38. struct edge2
  39. {
  40. inline void add_edge(int a,int b,int c)//加入一条边
  41. {
  42. g2[++tot2].x=a;//起点
  43. g2[tot2].y=b;//终点
  44. g2[tot2].w=c;//权值
  45. }
  46. inline int kruskal()
  47. {
  48. sort(g2+1,g2+1+tot2,cmp);//排序,找最小
  49. int ans=0;//我们的目标连通块的连通块编号
  50. for(int i=1; i<=tot2; i++)
  51. {
  52. int x=find(g2[i].x),y=find(g2[i].y);//求出所在连通块
  53. if (x==1 || y==1 || x==y)//不是目标连通块,或者已经在一起了
  54. continue;
  55. fa[x]=y;//合并
  56. ans+=g2[i].w;//统计
  57. q2[mk(g2[i].x,g2[i].y)]=true;//这条边出现过
  58. q2[mk(g2[i].y,g2[i].x)]=true;
  59. }
  60. return ans;
  61. }
  62. } g3;
  63. void read()
  64. {
  65. quick();
  66. init();
  67. cin>>n;
  68. root=q["Park"]=tot=1;//Park节点就是我们的一号节点
  69. for(int i=1; i<=n; i++)
  70. {
  71. cin>>a>>b>>c;
  72. if (!q[a])//名字读入
  73. q[a]=(++tot);//新编号
  74. if (!q[b])
  75. q[b]=(++tot);//新编号
  76. g3.add_edge(q[a],q[b],c);//加边
  77. add(q[a],q[b],c);//加边
  78. fa[i]=i;//初始化每个点的父亲节点
  79. }
  80. cin>>s;
  81. }
  82. void dfs(int x)
  83. {
  84. for(int j=2; j<=tot; j++)
  85. if (g[x][j] && !vis[j])//有边,但是木有被标记
  86. {
  87. vis[j]=cnt;
  88. dfs(j);
  89. }
  90. }
  91. void pd()//连通块划分
  92. {
  93. for(int i=2; i<=tot; i++)
  94. if (!vis[i])
  95. {
  96. cnt++;//又来一个
  97. vis[i]=cnt;
  98. dfs(i);
  99. }
  100. }
  101. void dfs(int now,int last)//计算(1,x)路径上最大边
  102. {
  103. for(int i=2; i<=tot; i++)
  104. {
  105. if(i==last || !q2[mk(now,i)])//点重叠,或者没有这条边
  106. continue;
  107. if(dp[i].d==-1)//没有来过
  108. {
  109. if(dp[now].d>g[now][i])
  110. dp[i]=dp[now];
  111. else
  112. {
  113. dp[i].u=now;
  114. dp[i].v=i;
  115. dp[i].d=g[now][i];
  116. }
  117. }
  118. dfs(i,now);
  119. }
  120. }
  121. void work()
  122. {
  123. pd();
  124. int ans=g3.kruskal();//统计每一个连通块的值
  125. for(int i=1; i<=cnt; i++)
  126. {
  127. int now=0x3f3f3f3f,st=0;//初始值为INF
  128. for(int j=2; j<=tot; j++)
  129. if (vis[j]==i)//属于这个连通块
  130. if (now>g[1][j] && g[1][j]!=0)
  131. {
  132. now=g[1][j];//找到与1相连最小的边
  133. st=j;
  134. }
  135. ans+=now;//将每一个连通块与1相连
  136. q2[mk(1,st)]=q2[mk(st,1)]=true;
  137. }
  138. int t=cnt;
  139. while(s>t)
  140. {
  141. s--;
  142. int now=0,idx=0;
  143. for(int i=1; i<=1100; i++)
  144. dp[i].inits();
  145. dfs(1,-1);//求每一个点到1的途中最大边是谁?
  146. for(int j=2; j<=tot; j++)
  147. {
  148. if(now<dp[j].d-g[1][j] && g[1][j])
  149. {
  150. now=dp[j].d-g[1][j];//找到最大权值边
  151. idx=j;
  152. }
  153. }
  154. if (now<=0)//已经不会多优秀了
  155. break;
  156. ans=ans-now;
  157. q2[mk(dp[idx].u,dp[idx].v)]=false;//删除边
  158. q2[mk(1,idx)]=q2[mk(idx,1)]=true;//添加边
  159. }
  160. cout<<"Total miles driven: "<<ans;
  161. }
  162. int main()
  163. {
  164. read();
  165. work();
  166. return 0;
  167. }

POJ1639 算法竞赛进阶指南 野餐规划的更多相关文章

  1. 《算法竞赛进阶指南》0x10 基本数据结构 Hash

    Hash的基本知识 字符串hash算法将字符串看成p进制数字,再将结果mod q例如:abcabcdefg 将字母转换位数字(1231234567)=(1*p9+2*p8+3*p7+1*p6+2*p5 ...

  2. 《算法竞赛进阶指南》1.4Hash

    137. 雪花雪花雪花 有N片雪花,每片雪花由六个角组成,每个角都有长度. 第i片雪花六个角的长度从某个角开始顺时针依次记为ai,1,ai,2,-,ai,6. 因为雪花的形状是封闭的环形,所以从任何一 ...

  3. bzoj 1787 && bzoj 1832: [Ahoi2008]Meet 紧急集合(倍增LCA)算法竞赛进阶指南

    题目描述 原题连接 Y岛风景美丽宜人,气候温和,物产丰富. Y岛上有N个城市(编号\(1,2,-,N\)),有\(N-1\)条城市间的道路连接着它们. 每一条道路都连接某两个城市. 幸运的是,小可可通 ...

  4. 算法竞赛进阶指南 0x00 基本算法

    放在原来这个地方不太方便,影响阅读体验.为了读者能更好的刷题,另起一篇随笔. 0x00 基本算法 0x01 位运算 [题目][64位整数乘法] 知识点:快速幂思想的灵活运用 [题目][最短Hamilt ...

  5. 算法竞赛进阶指南--快速幂,求a^b mod p

    // 快速幂,求a^b mod p int power(int a, int b, int p) { int ans = 1; for (; b; b >>= 1) { if (b &am ...

  6. 算法竞赛进阶指南0x14 Hash

    组成部分: 哈希函数: 链表 AcWing137. 雪花雪花雪花 因为所需要数据量过于大,所以只能以O(n)的复杂度. 所以不可能在实现的过程中一一顺时针逆时针进行比较,所以采用一种合适的数据结构. ...

  7. 《算法竞赛进阶指南》1.6Trie

    142. 前缀统计 给定N个字符串S1,S2-SN,接下来进行M次询问,每次询问给定一个字符串T,求S1-SN中有多少个字符串是T的前缀. 输入字符串的总长度不超过106,仅包含小写字母. 输入格式 ...

  8. 《算法竞赛进阶指南》 1 (P4) a^b 快速幂

    快速幂 #include<cstdio> #include<cmath> #include<iostream> using namespace std; long ...

  9. POJ1722 算法竞赛进阶指南 SUBSTRACT减操作

    原题连接 题目描述 给定一个整数数组\(a_1,a_2,-,a_n\). 定义数组第 i 位上的减操作:把\(a_i\)和\(a_{i+1}\)换成\(a_i - a_{i+1}\). 用con(a, ...

随机推荐

  1. HashMap、Hashtable 以及HashSet

    关于多线程的问题大多会涉及到Collection框架,涉及到Collection框架就不得不谈HashSet和HashMap.HashMap和HashSet都是collection框架的一部分,它们让 ...

  2. Linux中权限控制ACL命令

    很多小伙伴觉得,Linux的权限管理命令不就是chown和chmod命令吗,什么时候有了ACL了? 什么是ACLACL是访问控制列表(Access Control List)的缩写,主要的目的是在提供 ...

  3. Email功能的设定

    在前期制定发送邮件功能时,都是使用导入import smtplib模块来实现,虽说能实现, 但是对新人.刚接触的来说不太友好,虽然网上五花八门的教程一大推,但还是要自己花不少时间才搞定(笔者就是) 在 ...

  4. 【计算机视觉】行人检测(Pedestrian Detection)资源

    一.论文 综述类的文章 [1]P.Dollar, C. Wojek,B. Schiele, et al. Pedestrian detection: an evaluation of the stat ...

  5. addRoutes进行权限控制

    用addRoutes实现动态路由:https://www.jianshu.com/p/0bea4a1b0350 详解基于vue,vue-router, vuex以及addRoutes进行权限控制:ht ...

  6. web - code/flash

    trace 来源: 1. http://traces.cs.umass.edu/index.php/Storage/Storage 源代码: 1.sourceforge 2.github.github ...

  7. J-流浪西邮之寻找火石碎片 【经典背包变形】

    题目来源:2019 ACM ICPC Xi'an University of Posts & Telecommunications School Contest 链接:https://www. ...

  8. AMD平台如何使用Android Studio官方的高性能模拟器

    当我第一次接触Android Studio的时候,脑子里第一个想法是:tm不就是IDEA么??以为自己会用的贼六,结果其他小朋友的模拟器都打开了,才发现自己运行不了模拟器.一度以为是我哪里操作错了.于 ...

  9. find_in_set使用

    FIND_IN_SET(str,strList) str 要查询的字符串 strList 字段名,参数以“,”分隔,如(1,2,6,8) 查询字段(strList)中包含的结果,返回结果null或记录 ...

  10. python 高阶函数 lamdad reduce map

    ## def use_filer(l):## # 过滤偶数# rest = filter(lambda n: n % 2 != 0, l)# return rest## if __name__ == ...