week6作业

A - 氪金带东:
在这里插入图片描述
在这里插入图片描述
思路总结:这道题目是一道是求树中的一节点到其他任意节点的最远距离,而在树中有树的直径这么一个概念,指的是树中任意两点之间最远的距离。可以想到树的直径或许不止一条,但是树中还有另一个性质,对于任意节点来说,其到树中其他点的最远距离一定为该点到树的直径的某一端点的距离(在这里是无论与那条直径的),因此,根据此定义,我们可以首先找到1节点到另一节点的最远距离,此一步是为了获取树的直径的一个端点,在一步我们利用求得的端点进行dfs搜索获取另一个直径端点,这样,我们分别比较两个直径到其他点的距离,取最长的即可。另外,值得思考的是,在一棵树中,一个点到另一个点的路径只有一条,这也使得我们在获取距离时显得十分方便,这也是无环图的一个重要性质。在者,对于这道题,我们在获取距离时,有bfs和dfs两种方法可以选择,当经过比对,dfs的时间复杂度要优于bfs。

代码:

#include<iostream>
#include<stdio.h>
#include<cmath>
#include<string.h>
using namespace std;
struct edge {
	int u,v,w,nxt;
} Edge[20005];
int head[10005];
int tot;
int func[10005]= {0};
int func2[10005]= {0};
int func3[10005]= {0};
int vis[10005]={0};
void init(int n) {
	tot=0;
	for(int i=0; i<=n; i++)
		head[i]=-1;
}
void addedge(int u,int v,int w) {
	Edge[tot].u=u;
	Edge[tot].v=v;
	Edge[tot].w=w;
	Edge[tot].nxt=head[u];
	head[u]=tot;
	tot++;
}
void dfs(int* func,int x,int &y)
{
	vis[x]=1;
	for(int i=head[x];i!=-1;i=Edge[i].nxt)
	{
		if(vis[Edge[i].v]==0)
		{
			func[Edge[i].v]=func[x]+Edge[i].w;
			vis[Edge[i].v]=1;
			if(func[Edge[i].v]>func[y])
			y=Edge[i].v;
			dfs(func,Edge[i].v,y);
		}
	}
}
int main() {
	int n;
	while(scanf("%d",&n)!=EOF) {
		init(n);
		memset(vis,0,sizeof(vis));
		memset(func,0,sizeof(func));
		memset(func2,0,sizeof(func2));
		memset(func3,0,sizeof(func3));
		for(int i=2; i<=n; i++) {
			int a,b;
			scanf("%d%d",&a,&b);
			addedge(i,a,b);
			addedge(a,i,b);
		}
		int y=1;
		dfs(func,1,y);
		memset(vis,0,sizeof(vis));
		int w=y;
		dfs(func2,w,y);
		memset(vis,0,sizeof(vis));
		int z=y;
		dfs(func3,z,y);
		for(int i=1; i<=n; i++) {
			cout<<max(func2[i],func3[i])<<endl;
		}
	}
}在这里插入代码片

B - 戴好口罩!:
在这里插入图片描述
在这里插入图片描述
思路总结:
这道题目并不难,主要是对于并查集的使用,并查集是一种查考的数据结构类型,因此,对于该结构我们应当做到能够记住其代码。另外,对于并查集的查找问题,为避免极端情况的出现,我们采取路径压缩的办法,使得寻找节点的根节点比较容易,这其中的代码也是需要背记住的。
代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct point {
	int size;
	int father;
	point() {
		size=1;
		father=-1;
	}
} Point[30005];
int b[30005];
int find(int a) {
	if(Point[a].father==-1)
		return a;
	else
		return Point[a].father=find(Point[a].father);
}
void unit(int a,int b) {
	a=find(a);
	b=find(b);
	if(a!=b) {
		if(Point[a].size>Point[b].size) {
			Point[b].father=a;
			Point[a].size+=Point[b].size;
		} else {
			Point[a].father=b;
			Point[b].size+=Point[a].size;
		}
	}

}
int main() {
	int n,m;
	scanf("%d%d",&n,&m);
	while(!(n==0&&m==0)) {
		int a;
		for(int i=0; i<m; i++) {
			scanf("%d",&a);
			for(int i=0; i<a; i++) {
				scanf("%d",&b[i]);
				unit(b[0],b[i]);
			}
		}
		cout<<Point[find(0)].size<<endl;
		for(int i=0;i<30005;i++)
		{
			Point[i].father=-1;
			Point[i].size=1;
		}
		scanf("%d%d",&n,&m);
	}
}
在这里插入代码片

C - 掌握魔法の东东 I:
在这里插入图片描述
在这里插入图片描述
思路总结:
这道题目的意思是将各个端点联通起来,而连通图的所有边的权和的最小值即对应着最小生成树,在这道题目上面,我们对于零号远点的处理应当是一个亮点,这也值得我们去思考,之所以可以抽象作为0号原点,在于该点具有与其他点共同的特性,只不过特殊在该点与其他点都联通。对于最小生成树,有两种算法思想,都是基于贪心策略的,即kruskal与prim算法,其实对于两种算法,在做题时也应当存在一个选择问题,在点多边少情况下,我们通常会选取kruskal算法,反之,我们通常会选取prim算法。另外对于两种算法,其通常需要设置多少变量,具有哪些细节的处理,需要我们能够记住。
代码:

#include<iostream>
#include<stdio.h>
using namespace std;
int dis[301][301];
int val[301]={0};
bool label[301]={0};
int ans=0;
int func=1000000;
int func1=0;
int main()
{
	int n;
	dis[0][0]=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
	    scanf("%d",&dis[0][i]);
	    dis[i][0]=dis[0][i];
	    val[i]=dis[0][i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&dis[i][j]);
		}
	}
	label[0]=1;
	while(1)
	{
		func=1000000;
		for(int i=1;i<=n;i++)
		{
			if(label[i]==0&&val[i]<func)
			{
				func=val[i];
				func1=i;
			}
		}
		if(func==1000000)
		break;
		label[func1]=1;
		ans=ans+func;
		for(int i=1;i<=n;i++)
		{
			if(dis[i][func1]<val[i])
			val[i]=dis[i][func1];
		}
	}
	cout<<ans<<endl;
}
在这里插入代码片

D - 数据中心:
在这里插入图片描述
在这里插入图片描述思路总结:
对于这道题目来讲,其题意比较难以理解,但是仔细审题,会发现其还是一道求取最小生成树的问题,这是基于最小生成树的一个性质,最小生成树同时也是最小瓶颈生成树,在这个方面,我们联想kruskal算法的正确性再加上破圈理论即可证明,因此,对于本题而言,我们只需要按照通常算法进行最小生成树的求解即可,值得注意的是,该题目利用prim算法会超时,这是因为点多的缘故,故只能采取kruskal的算法。在这道题目上,自己感触最深的是,我们不能够简单的知道某一算法的正确性,更重要的是理解其为什么具有正确性,深入的理解会使我们对其性质有更深的理解,方可使得我们能够在做题时最快的联想到其中的思想。

代码:

#include<iostream>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;
struct node{
	int begin,end,value;
	node(){
	}
	node(int beg,int e,int val)
	{
		begin=beg;
		end=e;
		value=val;
	}
	bool operator<(node& p)
	{
		if(value!=p.value)
		return value<p.value;
		else if(begin!=p.begin) 
		return begin<p.begin;
		else
		return end<p.end;
	} 
};
int label[50005];
node no[100005];
int find(int a)
{
	if(label[a]==-1)
	return a;
	else
	return label[a]=find(label[a]);
}
int unit(int a,int b)
{
	a=find(a);
	b=find(b);
	if(a==b)
	return 0;
	else
	label[a]=b;
	return 1;
}
int main()
{
	memset(label,-1,sizeof(label));
	int n,m,root;
	scanf("%d%d%d",&n,&m,&root);
	int a,b,c;
	for(int i=0;i<m;i++)
	{
		scanf("%d%d%d",&no[i].begin,&no[i].end,&no[i].value);
	}
	sort(no,no+m);
	int func=0;
	int ans=0;
    for(int i=0;i<m;i++)
    {
    	int a=no[i].begin;
    	int b=no[i].end;
    	if(unit(a,b))
    	{
    		func++;
    		ans=no[i].value;
		}
		if(func==n-1)
		break;
	}
	cout<<ans<<endl;
}在这里插入代码片
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值