题目描述

原题链接

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

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

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

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

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

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

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

输入格式

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

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

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

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

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

输出格式

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

输入样例:

10
Alphonzo Bernardo 32
Alphonzo Park 57
Alphonzo Eduardo 43
Bernardo Park 19
Bernardo Clemenzi 82
Clemenzi Park 65
Clemenzi Herb 90
Clemenzi Eduardo 109
Park Herb 24
Herb Eduardo 79
3

输出样例:

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
\]

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


代码解释

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

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

#include <bits/stdc++.h>
using namespace std;
const int N=1010;
#define quick() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)//读入优化
#define mem(a,b) memset(a,b,sizeof(a))//初始化
#define init() mem(g,0),mem(vis,false),root=cnt=tot2=tot=0,q.clear(),q2.clear()//初始化大军
#define add(a,b,c) g[a][b]=g[b][a]=c//无向图加边
#define mk(a,b) make_pair(a,b)//简便
int tot2,tot,n,m,fa[N],cnt,c,s,root,g[31][31],dis[31][31];
//tot2为边的数量
//tot为点的数量
//cnt为编号的数量
map<string,int> q;//映射关系
map<pair<int,int>,bool> q2;//统计这条边是否出现在最小生成树中
int vis[N];//标记连通块的编号
string a,b;
int find(int x)
{
return x==fa[x]?fa[x]:fa[x]=find(fa[x]);//并查集找红太阳
}
struct edge1
{
int x,y,w;
} g2[N];
int cmp (edge1 a,edge1 b)
{
return a.w<b.w;//最小边排序
}
struct node
{
int u,v,d;//(u,v)节点权值为d
inline void inits()
{
u=v=0;
d=-1;
}
} dp[N];
struct edge2
{
inline void add_edge(int a,int b,int c)//加入一条边
{
g2[++tot2].x=a;//起点
g2[tot2].y=b;//终点
g2[tot2].w=c;//权值
}
inline int kruskal()
{
sort(g2+1,g2+1+tot2,cmp);//排序,找最小
int ans=0;//我们的目标连通块的连通块编号
for(int i=1; i<=tot2; i++)
{
int x=find(g2[i].x),y=find(g2[i].y);//求出所在连通块
if (x==1 || y==1 || x==y)//不是目标连通块,或者已经在一起了
continue;
fa[x]=y;//合并
ans+=g2[i].w;//统计
q2[mk(g2[i].x,g2[i].y)]=true;//这条边出现过
q2[mk(g2[i].y,g2[i].x)]=true;
}
return ans;
}
} g3;
void read()
{
quick();
init();
cin>>n;
root=q["Park"]=tot=1;//Park节点就是我们的一号节点
for(int i=1; i<=n; i++)
{
cin>>a>>b>>c;
if (!q[a])//名字读入
q[a]=(++tot);//新编号
if (!q[b])
q[b]=(++tot);//新编号
g3.add_edge(q[a],q[b],c);//加边
add(q[a],q[b],c);//加边
fa[i]=i;//初始化每个点的父亲节点
}
cin>>s;
}
void dfs(int x)
{
for(int j=2; j<=tot; j++)
if (g[x][j] && !vis[j])//有边,但是木有被标记
{
vis[j]=cnt;
dfs(j);
}
}
void pd()//连通块划分
{
for(int i=2; i<=tot; i++)
if (!vis[i])
{
cnt++;//又来一个
vis[i]=cnt;
dfs(i);
}
}
void dfs(int now,int last)//计算(1,x)路径上最大边
{
for(int i=2; i<=tot; i++)
{
if(i==last || !q2[mk(now,i)])//点重叠,或者没有这条边
continue;
if(dp[i].d==-1)//没有来过
{
if(dp[now].d>g[now][i])
dp[i]=dp[now];
else
{
dp[i].u=now;
dp[i].v=i;
dp[i].d=g[now][i];
}
}
dfs(i,now);
}
}
void work()
{
pd();
int ans=g3.kruskal();//统计每一个连通块的值
for(int i=1; i<=cnt; i++)
{
int now=0x3f3f3f3f,st=0;//初始值为INF
for(int j=2; j<=tot; j++)
if (vis[j]==i)//属于这个连通块
if (now>g[1][j] && g[1][j]!=0)
{
now=g[1][j];//找到与1相连最小的边
st=j;
}
ans+=now;//将每一个连通块与1相连
q2[mk(1,st)]=q2[mk(st,1)]=true;
}
int t=cnt;
while(s>t)
{
s--;
int now=0,idx=0;
for(int i=1; i<=1100; i++)
dp[i].inits();
dfs(1,-1);//求每一个点到1的途中最大边是谁?
for(int j=2; j<=tot; j++)
{
if(now<dp[j].d-g[1][j] && g[1][j])
{
now=dp[j].d-g[1][j];//找到最大权值边
idx=j;
}
}
if (now<=0)//已经不会多优秀了
break;
ans=ans-now;
q2[mk(dp[idx].u,dp[idx].v)]=false;//删除边
q2[mk(1,idx)]=q2[mk(idx,1)]=true;//添加边
}
cout<<"Total miles driven: "<<ans;
}
int main()
{
read();
work();
return 0;
}

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. Unreal Engine* 4/英特尔® VTune™ Amplifier 使用指南

    借助英特尔 VTune Amplifier,可以通过单一易用的分析界面获得先进的分析功能.UE4 和英特尔 VTune Amplifier 相互配合,支持调查代码并进行分析,从而在多个内核上顺畅运行. ...

  2. python之xml数据解析

    因为项目需求需要查询一些网站的ALEXA排名,百度后得到的方法是,访问http://data.alexa.com/data?cli=10&dat=snbamz&url=%YOURURL ...

  3. Leetcode之动态规划(DP)专题-309. 最佳买卖股票时机含冷冻期(Best Time to Buy and Sell Stock with Cooldown)

    Leetcode之动态规划(DP)专题-309. 最佳买卖股票时机含冷冻期(Best Time to Buy and Sell Stock with Cooldown) 股票问题: 121. 买卖股票 ...

  4. 【VS开发】【Live555-rtsp】在windows 使用vs2008编译live555

    在windows 使用vs2008编译live555 基于 liveMedia的程序,需要通过继承UsageEnvironment抽象类和TaskScheduler抽象类,定义相应的类来处理事件调度, ...

  5. 随笔--第一次使用crontab linux选择编辑器问题

    第一次使用crontab 时,会出现:no crontab for root - using an empty one “Select a editor ......”下面有几个选项,就是叫你选择编辑 ...

  6. js 返回一个数组里面0出现的次数

    var num = new Array(10000).fill('').map((item,index) => (index + 1)). 在点号后面补充代码,让num是这个数组中0出现的次数, ...

  7. 小记--------spark的宽依赖与窄依赖分析

    窄依赖: Narrow Dependency : 一个RDD对它的父RDD,只有简单的一对一的依赖关系.RDD的每个partition仅仅依赖于父RDD中的一个partition,父RDD和子RDD的 ...

  8. Hive远程连接

    HIVE的连接模式== 本地连接模式 直接启动hive命令 HIVE的远程连接 这里要启动HIVE的服务 thirft进行编写 hiveserver2 —- > 前台启动 后台启动 前台启动 h ...

  9. django ajax MTV与MVC 多对多创建方式

    MTV与MVC MTV模型(django): M:模型层(models.py) T:templates V:views MVC模型: M:模型层(models.py) V:视图层(views.py) ...

  10. webpack的postcss的基本应用

    PostCss是什么? PostCSS在webpack中的基本应用 一.PostCss是什么? 如果有深入学习PostCss需求的话可以参考大漠的资料:https://www.w3cplus.com/ ...