NOIP 2018 提高组 复赛 第一天 第三题 赛道修建 track 5分代码(Floyd)+20分代码(菊花图,树的直径,链(二分))

NOIP 2018 提高组 复赛 第一天 第三题 赛道修建  track   5分代码(Floyd)+20分代码(菊花图,树的直径,链(二分))

总目录详见:NOIP 提高组 复赛 试题 目录 信奥 历年

在线测评地址:https://www.luogu.com.cn/problem/P5021

1.5分代码(floyd算法):

针对测试点1进行编写

发现是先用Floyd算法,在树上计算最短路径,再寻找最短路径中的最大距离。

5分代码如下

#include <stdio.h>
#define INF 500000010
int n,m;
int map[110][110];
int main(){
	int i,j,k,a,b,l,mx;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)//map初始化
		for(j=1;j<=n;j++)
			if(i==j)map[i][j]=0;
			else map[i][j]=INF;
	for(i=1;i<n;i++){
		scanf("%d%d%d",&a,&b,&l);
		map[a][b]=l,map[b][a]=l;
	}
	for(k=1;k<=n;k++)//Floyd算法
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
				if(map[i][j]>map[i][k]+map[k][j])
					map[i][j]=map[i][k]+map[k][j];
	mx=0;
	for(i=1;i<=n;i++)//寻找最大距离
		for(j=1;j<=n;j++)
			if(map[i][j]!=INF&&mx<map[i][j])
				mx=map[i][j];
	printf("%d\n",mx);
	return 0;
}

2.20代码(ai​=1的情况(菊花图))

解法:把所有边权记录下来,从大到小排序。设边权为 w,答案即为 w[1]+w[2m],w[2]+w[2m-1],...,w[m]+w[m+1]的最小值,时间复杂度 O(nlogn),

该算法的难点在于,若w数组长度太短,即n-1<2*m的处理上,可以将数组加长,自大到小排序后,多出的部分,用0补齐

20分 菊花图 代码如下

#include <cstdio>
#include <algorithm>
#define maxn 50010
using namespace std;
int n,m,head[maxn],tot;
struct node{
	int to,next,w;
}e[maxn<<1];
void add_edge(int u,int v,int w){
	tot++,e[tot].to=v,e[tot].next=head[u],e[tot].w=w,head[u]=tot;
}
int cmp(node a,node b){
	return a.w>b.w;
}
int main(){
	int u,v,w,i,a1,ans=2000000000;
	scanf("%d%d",&n,&m);
	a1=1;
	for(i=1;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);
		if(u!=1)a1=0;
		add_edge(u,v,w);
		add_edge(v,u,w);
	}
	if(a1==1){
		for(i=1;i<n;i++)e[i*2-1].w=0;//让2*(n-1)中的n-1部分数据为0 
		sort(e+1,e+2*n-1,cmp);//自大到小排序 
		for(i=1;i<=m;i++)ans=min(ans,e[i].w+e[2*m-i+1].w);
		printf("%d\n",ans);
	}	
	return 0;
} 

3.20代码(树的直径)

树的直径学习

该项讨论m==1的情况。

20分 树的直径 代码如下

#include <stdio.h>
#define maxn 50010
int n,m,head[maxn],tot,ans,d[maxn],f_num;
struct node{
	int to,next,w;
}e[maxn<<1];
void add_edge(int u,int v,int w){
	tot++,e[tot].to=v,e[tot].next=head[u],e[tot].w=w,head[u]=tot;
}
void dfs(int u,int fa){
	int b,v,w;
	if(ans<d[u])ans=d[u],f_num=u; 
	for(b=head[u];b;b=e[b].next){
		v=e[b].to,w=e[b].w;
		if(v==fa)continue;
		d[v]=d[u]+w;
		dfs(v,u);
	}
}
int main(){
	int u,v,w,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);
		add_edge(u,v,w);
		add_edge(v,u,w);
	}
	if(m==1){
		ans=0,d[1]=0;
		dfs(1,0);
		ans=0,d[f_num]=0;
		dfs(f_num,0);
		printf("%d\n",ans);
	}	
	return 0;
} 

4.20分代码(bi​=ai​+1 的情况(一条链))

解法:把所有边权记录下来,这种情况等价于将序列分割成 m 段,使 m 段区间和的最小值最大。

那么二分 m 段区间和的最小值,然后 O(n)贪心扫一遍,时间复杂度 O(nlogn)

该做法的难点是,读入数据顺序不定,需重新深搜遍历,读出相应的链式结构。

20分 链 代码如下

#include <cstdio>
#include <algorithm>
#define maxn 50010
using namespace std;
int n,m,head[maxn],tot,sum,a[maxn];
struct node{
	int to,next,w;
}e[maxn<<1];
void add_edge(int u,int v,int w){
	tot++,e[tot].to=v,e[tot].next=head[u],e[tot].w=w,head[u]=tot;
}
void dfs(int u,int fa){//找回链式结构 
	int b,v;
	for(b=head[u];b;b=e[b].next){
		v=e[b].to;
		if(v==fa)continue;
		a[u]=e[b].w; 
		dfs(v,u);
	}
}
int judge(int x){
	int now,t,i;
	now=0,t=0;
	for(i=1;i<n;i++){
		if(now+a[i]>=x)t++,now=0;
		else now+=a[i];
	}
	return t>=m;
}
void solve(){
	int l,r,mid;
	l=1,r=sum+1;
	while(l+1<r){
		mid=(l+r)/2;
		if(judge(mid))l=mid;
		else r=mid;
	}
	printf("%d\n",l);
}
int main(){
	int u,v,w,i,lian=1;
	scanf("%d%d",&n,&m);
	for(i=1;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);
		if(v!=u+1)lian=0;
		add_edge(u,v,w);
		add_edge(v,u,w);
		sum+=w;
	}
	if(lian==1){
		dfs(1,0);
		solve();
	}	
	return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值