今天把最小生成树的两种算法全部写了一遍,而且还顺便学了一下快排
哈哈哈哈,果然是昨天的脑子不好今天好多了
首先我来简单的讲一下快速排序吧
我们以前用的暴力排序的算法如冒泡和插入都是时间复杂度较高的所以为了改进就去学了快排(因为写题目要排序,时间总是爆掉)
快排的原理是分化(用递归来实现的)
在一次的递归的过程中以一个数作为标杆把数组分为大于它的和小于它的
具体的首先就是
1把数组(你要排序的那一段)的前后各放一个标,我们把第一个选为标杆,用一个变量保存(相当于把第一个先拿掉),
2从后面的标往前移碰到比标干小的就把它赋给前标对应的数(很明现如果是第一个,那就是赋给数组的头)
3然后接下来移动前标找到比标杆大的数赋给后标对应的数(很显然后标对应的数是刚刚小于标杆的数被赋给了第一个也相当于空缺了)
4前后标移动的时候要注意不可以交叉也就是说当前后标相遇时就是结束的时候,此时把标杆赋给这个位置就完成了
5而这个相于的位置就是分割点把数组分开成两部分再重复上面的操作,最后就排好了。
很明显由于数的交换是有跨度的所以它的时间复杂度要比暴力排序要快的多
上点代码方便食用
int quckfind(int a[],int k,int l){
int u=a[k];//以第一个数为标点
while(k<l){
while(a[l]>=u&&l>k){
l--;
}
a[k]=a[l];
while(a[k]<=u&&k<l){
k++;}
a[l]=a[k];
}
a[l]=u;
return l;
}
void qucksort(int a[],int x,int y){
if(x<y)
{
int point=quckfind(a,x,y);//找到支点的位置用于分割
qucksort(a,x,point-1);//分而搞定
qucksort(a,point+1,y);
}
}
代码主要就是一个递归的思想了,很简单的啦,如果对过程不了解的可以再本子上画一下理解一下。
我还写了一个结构体数组的排序也拿出来看看其实大致一样的,就是用指针传入参数
#include<stdio.h>
struct node{
int x;
int y;
int m;
}dian[50000];
int quckfind(struct node *pt,int k,int l){
int u=(pt+k)->m;//以第一个数为标点
int ul=(pt+k)->x;
int uj=(pt+k)->y;
while(k<l){
while((pt+l)->m>=u&&l>k){
l--;
}
(pt+k)->m=(pt+l)->m;
(pt+k)->x=(pt+l)->x;
(pt+k)->y=(pt+l)->y;
while((pt+k)->m<=u&&k<l){
k++;}
(pt+l)->m=(pt+k)->m;
(pt+l)->x=(pt+k)->x;
(pt+l)->y=(pt+k)->y;
}
(pt+k)->m=u;
(pt+k)->x=ul;
(pt+k)->y=uj;
return k;
}
void qucksort(struct node* pl,int x,int y){
if(x<y)
{
int point=quckfind(pl,x,y);//找到支点的位置用于分割
qucksort(pl,x,point-1);//分而搞定
qucksort(pl,point+1,y);
}
}
int main(){
for(int l=1;l<=5;l++){
scanf("%d%d%d",&dian[l].x,&dian[l].y,&dian[l].m);
}
qucksort(dian,1,5);
for(int j=1;j<=5;j++){
printf("%d %d\n",dian[j].x,dian[j].m);
}
return 0;
}
由于用的是指针看起来不太一样,其实内容完全一致,这是拿结构体其中的一个变量来排序,也可以是两个,或者是通过处理来比较,可变性较高的。
来点题目
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz
。
输入格式
第一行包含两个整数 N,MN,M,表示该图共有 NN 个结点和 MM 条无向边。
接下来 MM 行每行包含三个整数 X_i,Y_i,Z_iXi,Yi,Zi,表示有一条长度为 Z_iZi 的无向边连接结点 X_i,Y_iXi,Yi。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
输入输出样例
输入 #1复制
4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3
输出 #1复制
7
说明/提示
数据规模:
对于 20\%20% 的数据,N\le 5N≤5,M\le 20M≤20。
对于 40\%40% 的数据,N\le 50N≤50,M\le 2500M≤2500。
对于 70\%70% 的数据,N\le 500N≤500,M\le 10^4M≤104。
对于 100\%100% 的数据:1\le N\le 50001≤N≤5000,1\le M\le 2\times 10^51≤M≤2×105,1\le Z_i \le 10^41≤Zi≤104。
样例解释:
所以最小生成树的总边权为 2+2+3=72+2+3=7。
这题目昨天写了,因为内存没过我今天看看了一下,把储存线长度的数组从long long 改成int 就过了,想看的可以看我昨天的写的
下一个
题目描述
国防部计划用无线网络连接若干个边防哨所。2 种不同的通讯技术用来搭建无线网络;
每个边防哨所都要配备无线电收发器;有一些哨所还可以增配卫星电话。
任意两个配备了一条卫星电话线路的哨所(两边都ᤕ有卫星电话)均可以通话,无论他们相距多远。而只通过无线电收发器通话的哨所之间的距离不能超过 DD,这是受收发器的功率限制。收发器的功率越高,通话距离 DD 会更远,但同时价格也会更贵。
收发器需要统一购买和安装,所以全部哨所只能选择安装一种型号的收发器。换句话说,每一对哨所之间的通话距离都是同一个 DD。你的任务是确定收发器必须的最小通话距离 DD,使得每一对哨所之间至少有一条通话路径(直接的或者间接的)。
输入格式
从 wireless.in 中输入数据第 1 行,2 个整数 SS 和 PP,SS 表示可安装的卫星电话的哨所数,PP 表示边防哨所的数量。接下里 PP 行,每行两个整数 x,yx,y 描述一个哨所的平面坐标 (x, y)(x,y),以 km 为单位。
输出格式
输出 wireless.out 中
第 1 行,1 个实数 DD,表示无线电收发器的最小传输距离,精确到小数点后两位。
输入输出样例
输入 #1复制
2 4 0 100 0 300 0 600 150 750
输出 #1复制
212.13
说明/提示
对于 20\%20% 的数据:P = 2,S = 1P=2,S=1
对于另外 20\%20% 的数据:P = 4,S = 2P=4,S=2
对于 100\%100% 的数据保证:1 ≤ S ≤ 1001≤S≤100,S < P ≤ 500S<P≤500,0 ≤ x,y ≤ 100000≤x,y≤10000。
出自:P1991 无线通讯网 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
我们先不考虑卫星电话,那题目就是问你最小生成树的最后一条边的权值
但是呀多了电话,
为了尽量传输距离小那卫星电话肯定给较长的啦
所以每多一台卫星电话最长的那条边就会被卫星电话取代,传输距离要求就会减小。
那还有啥可讲的啦
先把最小生成树的每条边都找到,然后排序取下标为(n-卫星电话台数)的那个就是答案
上码!
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
double max=99999999;//边界
int map[505];//打标记
double maps[505][505];//存入线的长度
double a[505],b[505];
double dis[505];//记录最小距离
double ans[505];
int i=0;
int next;
//快排
int quckfind(double a[],int k,int l){
double u=a[k];//以第一个数为标点
while(k<l){
while(a[l]>=u&&l>k){
l--;
}
a[k]=a[l];
while(a[k]<=u&&k<l){
k++;}
a[l]=a[k];
}
a[l]=u;
return l;
}
void qucksort(double a[],int x,int y){
if(x<y)
{
int point=quckfind(a,x,y);//找到支点的位置用于分割
qucksort(a,x,point-1);//分而搞定
qucksort(a,point+1,y);
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int y=1;y<=m;y++)
{
scanf("%lf%lf",&a[y],&b[y]);
dis[y]=max;
}
for(int y=1;y<=m;y++)
{
for(int h=1;h<=m;h++){//算出每条边的长度
double jl=a[y]-a[h];
double jk=b[y]-b[h];
jl=jl*jl;
jk=jk*jk;
maps[y][h]=sqrt(jl+jk);
maps[h][y]=maps[y][h];
}
}
map[1]=1;
next=1;
for(int jo=1;jo<=m;jo++){
dis[jo]=maps[1][jo];
}
for(int jk=1;jk<m;jk++){
double min=999999.99;
for(int p=1;p<=m;p++){
if(dis[p]<min&&map[p]==0){
next=p;
min=dis[p];
}
}
map[next]=1;//打标记
ans[i]=min;
i++;
for(int jh=1;jh<=m;jh++){
if(dis[jh]>maps[next][jh]&&map[jh]==0){
dis[jh]=maps[next][jh];
}
}
}
qucksort(ans,0,i-1);
printf("%.2lf",ans[m-n-1]);
return 0;
}
最后的m-1-n是n是卫星电话数量,m-1是有多少条边
依旧是prim算法哈哈 排序用的快排如果用暴力多半过不了
下一个
题目背景
还记得 NOIP 2011 提高组 Day1 中的铺地毯吗?时光飞逝,光阴荏苒,三年过去了。组织者精心准备的颁奖典礼早已结束,留下的则是被人们踩过的地毯。请你来解决类似于铺地毯的另一个问题。
题目描述
会场上有 n 个关键区域,不同的关键区域由 m 条无向地毯彼此连接。每条地毯可由三个整数 u、v、w 表示,其中 u 和 v 为地毯连接的两个关键区域编号,w 为这条地毯的美丽度。
由于颁奖典礼已经结束,铺过的地毯不得不拆除。为了贯彻勤俭节约的原则,组织者被要求只能保留 K 条地毯,且保留的地毯构成的图中,任意可互相到达的两点间只能有一种方式互相到达。换言之,组织者要求新图中不能有环。现在组织者求助你,想请你帮忙算出这 K 条地毯的美丽度之和最大为多少。
输入格式
第一行包含三个正整数 n、m、K。
接下来 m 行中每行包含三个正整数 u、v、w。
输出格式
只包含一个正整数,表示这 K 条地毯的美丽度之和的最大值。
输入输出样例
输入 #1复制
5 4 3 1 2 10 1 3 9 2 3 7 4 5 3
输出 #1复制
22
说明/提示
选择第 1、2、4 条地毯,美丽度之和为 10 + 9 + 3 = 22。
若选择第 1、2、3 条地毯,虽然美丽度之和可以达到 10 + 9 + 7 = 26,但这将导致关键区域 1、2、3 构成一个环,这是题目中不允许的。
1<=n,m,k<=100000
出自:P2121 拆地毯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这个呀也是最小生成树只是要求变了一下变成了要最大的权值了
那就叫最大生成树吧(哈哈哈嗝)
而且它只要保留k条边了,而且这k条边不一定成'树”唯一就是规定不可以成环
哦!我的父神!prim算法无能为力了,没办法了快使用kruskal算法
而且很明显的和kruskal算法非常符合我们只要把当前最大的边一条一条的放进去并且每次检查是否会形成环路就可以了。
思路一下就完全大开了
ok上代码!
#include<stdio.h>
int fa[100100];
struct node{
int x;
int y;
int m;
}ditan[100100];
long long ans;
int quckfind(struct node *pt,int k,int l){//结构体快排,传入指针作为参数
int u=(pt+k)->m;//以第一个数为标点
int ul=(pt+k)->x;
int uj=(pt+k)->y;
while(k<l){
while((pt+l)->m>=u&&l>k){
l--;
}
(pt+k)->m=(pt+l)->m;
(pt+k)->x=(pt+l)->x;
(pt+k)->y=(pt+l)->y;
while((pt+k)->m<=u&&k<l){
k++;}
(pt+l)->m=(pt+k)->m;
(pt+l)->x=(pt+k)->x;
(pt+l)->y=(pt+k)->y;
}
(pt+k)->m=u;
(pt+k)->x=ul;
(pt+k)->y=uj;
return k;
}
void qucksort(struct node* pl,int x,int y){
if(x<y)
{
int point=quckfind(pl,x,y);//找到支点的位置用于分割
qucksort(pl,x,point-1);//分而搞定
qucksort(pl,point+1,y);
}
}
int find(int x){//寻组函数
if(fa[x]==x){
return x;
}
else{
fa[x]=find(fa[x]);
return fa[x];
}
}
void bg(int x){//初始数组
for(int u=1;u<=x;u++){
fa[u]=u;
}
}
void hebing(int x,int y){//合并
int jk=find(x);
int jl=find(y);
fa[jk]=jl;
}
int main(){
int n,c,k;
scanf("%d%d%d",&n,&c,&k);
bg(n);
for(int l=1;l<=c;l++){
scanf("%d%d%d",&ditan[l].x,&ditan[l].y,&ditan[l].m);
}
int ge=0;
qucksort(ditan,1,c);
for(int h=c;h>=1&&ge<k;h--){
int l=find(ditan[h].x);
int jl=find(ditan[h].y);//判断是否成环了
if(l!=jl){
hebing(ditan[h].x,ditan[h].y);
ge++;
ans+=ditan[h].m;
}
}
printf("%lld",ans);
return 0;
}
没办法啦要保存点和长度为了方边就用了结构体数组(用二维数组就很难排序了),检查是否成环就用查并集呀,简简单单的啦
啥你说你不会!快去b站学!成环就不方不成就方理解起来简单的雅痞
ok今天真不戳!
我们明天见!