POJ1639 算法竞赛进阶指南 野餐规划
题目描述
一群小丑演员,以其出色的柔术表演,可以无限量的钻进同一辆汽车中,而闻名世界。
现在他们想要去公园玩耍,但是他们的经费非常紧缺。
他们将乘车前往公园,为了减少花费,他们决定选择一种合理的乘车方式,可以使得他们去往公园需要的所有汽车行驶的总公里数最少。
为此,他们愿意通过很多人挤在同一辆车的方式,来减少汽车行驶的总花销。
由此,他们可以很多人驾车到某一个兄弟的家里,然后所有人都钻进一辆车里,再继续前进。
公园的停车场能停放的车的数量有限,而且因为公园有入场费,所以一旦一辆车子进入到公园内,就必须停在那里,不能再去接其他人。
现在请你想出一种方法,可以使得他们全都到达公园的情况下,所有汽车行驶的总路程最少。
输入格式
第一行包含整数\(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个节点相连.
思路确定
题意就已经告诉我们,我们的必备算法必然是最小生成树.但是具体的算法流程,我们还得思考一下.
首先,我们要知道两个性质.
一个最小生成树,它的任意一棵子树都是最小生成树.
也就是一个最小生成树,实际上是由很多棵最小生成树构成的.
根据上面所言的性质,我们可以这么考虑这道题目.
首先我们不妨不去考虑这个有特殊限制的一号节点,那么我们忽略掉这个一号节点后.
原图变成了\(T\)个连通块.
\]
那么我们对于每一个连通块,都可以在这个连通块内部求出它的最小生成树.
我们接下来再来考虑,如何让这些连通块与我们的一号节点相连,构成我们题目的最小生成树.
首先我们很容易求出一个相对较小的生成树.切记不是最小生成树
对于每一个连通块而言,显然要在每一个连通块之中选出一个节点与我们的一号节点的权值最小.即\((1,p)\)的权值要尽量小.
\]
那么此时,我们发现我们成功将节点们连接在一起了,构成了一个最小生成树,那么现在的问题就是,如何优化我们的生成树,将其变成我们的最小生成树.
我们现在知道和一号节点连接的点,一共有\(T\)个,但是题目中要求不多于\(S\)个节点就好了.
分类讨论一下
\]
那么我们必然就是无解情况.
\]
那么此时我们的生成树,就是我们的最小生成树.
\]
**我们发现,对于一个节点而言,它不一定要属于自己原本的连通块,它可以和节点\(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)\)
直到
\]
也就是不可以加边,或者加边已经木有意义了.
代码解释
这道题目的思路,花了我十五分钟不到的时间理解,然后代码花费了两个晚上的时间,尤其是中途花了一个晚上的时间,打了一百五十多行代码,就在要打完的时候,电脑挂掉了,然后没有保存........................
痛不欲生的我,再次打了一遍.时间就这样多了一晚上
#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 算法竞赛进阶指南 野餐规划的更多相关文章
- 《算法竞赛进阶指南》0x10 基本数据结构 Hash
Hash的基本知识 字符串hash算法将字符串看成p进制数字,再将结果mod q例如:abcabcdefg 将字母转换位数字(1231234567)=(1*p9+2*p8+3*p7+1*p6+2*p5 ...
- 《算法竞赛进阶指南》1.4Hash
137. 雪花雪花雪花 有N片雪花,每片雪花由六个角组成,每个角都有长度. 第i片雪花六个角的长度从某个角开始顺时针依次记为ai,1,ai,2,-,ai,6. 因为雪花的形状是封闭的环形,所以从任何一 ...
- bzoj 1787 && bzoj 1832: [Ahoi2008]Meet 紧急集合(倍增LCA)算法竞赛进阶指南
题目描述 原题连接 Y岛风景美丽宜人,气候温和,物产丰富. Y岛上有N个城市(编号\(1,2,-,N\)),有\(N-1\)条城市间的道路连接着它们. 每一条道路都连接某两个城市. 幸运的是,小可可通 ...
- 算法竞赛进阶指南 0x00 基本算法
放在原来这个地方不太方便,影响阅读体验.为了读者能更好的刷题,另起一篇随笔. 0x00 基本算法 0x01 位运算 [题目][64位整数乘法] 知识点:快速幂思想的灵活运用 [题目][最短Hamilt ...
- 算法竞赛进阶指南--快速幂,求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 ...
- 算法竞赛进阶指南0x14 Hash
组成部分: 哈希函数: 链表 AcWing137. 雪花雪花雪花 因为所需要数据量过于大,所以只能以O(n)的复杂度. 所以不可能在实现的过程中一一顺时针逆时针进行比较,所以采用一种合适的数据结构. ...
- 《算法竞赛进阶指南》1.6Trie
142. 前缀统计 给定N个字符串S1,S2-SN,接下来进行M次询问,每次询问给定一个字符串T,求S1-SN中有多少个字符串是T的前缀. 输入字符串的总长度不超过106,仅包含小写字母. 输入格式 ...
- 《算法竞赛进阶指南》 1 (P4) a^b 快速幂
快速幂 #include<cstdio> #include<cmath> #include<iostream> using namespace std; long ...
- POJ1722 算法竞赛进阶指南 SUBSTRACT减操作
原题连接 题目描述 给定一个整数数组\(a_1,a_2,-,a_n\). 定义数组第 i 位上的减操作:把\(a_i\)和\(a_{i+1}\)换成\(a_i - a_{i+1}\). 用con(a, ...
随机推荐
- CentOS Config FTP Server with SSL support
- Python3 Selenium自动化web测试 ==> 第六节 WebDriver高级应用 -- 操作web页面的滚动条
学习目的: 掌握页面元素定位以外的其他重要知识点. 正式步骤: 测试Python3代码 # -*- coding:utf-8 -*- from selenium import webdriver fr ...
- CRLF注入漏洞
CRLF是“回车+换行”(\r\n,%0d%0a)的简称. HTTP协议中,HTTP Header之间以一个CRLF分隔,Header与Body以两个CRLF分隔.URL重定向通常通过响应头中的Loc ...
- Java学习笔记-Java中的常用类
Java中有很多类是很常用的,此处列举System,Runtime,Date,Calendar,Math System System:类中的方法和属性都是静态的 字段摘要 static PrintSt ...
- Linux下使用 minicom 自动重复发送数据的实现
目录 在minicom中添加脚本路径 编写脚本 执行脚本 一个项目里要用的设备需要用串口来模拟传感器来测试,还是Linux下的. 串口助手cutecom很好用,但是不能定时发送数据. 所以用下面这个脚 ...
- [loj#2005][SDOI2017]相关分析 _线段树
「SDOI2017」相关分析 题目链接:https://loj.ac/problem/2005 题解: 把上面的式子拆掉,把下面的式子拆掉. 发现所有的东西都能用线段树暴力维护. 代码: #inclu ...
- winform串口控件serialPort1的使用
serialPort1 控件使用的关键点主要有三: 1.配置串口号2.配置数据接收事件3.打开串口 关键代码如下: private void Form1_Load(object sender, Eve ...
- linux kprobe rootkit学习
介绍 <linux二进制分析>中提到了使用kprobe来写内核rootkit,还给出了一个简单的源码实现,这里看一下他的源码 kprobe kprobe的介绍可以看下面这几篇文章 介绍:h ...
- 高性能MySQL3_笔记0
该书2015年5月出版的,实际上已经有些老了,但是经典的东西还是经典. 该书一共16章 1.Mysql的架构与历史 2.Mysql基准测试 3.服务器性能剖析 4.Schema与数据类型优化 5.创建 ...
- 进阶Java编程(7)反射机制
反射机制 1,反射机制简介 在Java语言里面之所以会有如此多的开源技术支撑,很大的一部分是来自于Java最大的特征[反射机制].如果你不能够使用反射机制去进行项目的开发与设计,那么可以说你并未接触到 ...