SDU程序设计思维与实践作业Week6

A-氪金带东

题目

题目
Input&&Output:
INPUT&&OUTPUT
Sample:

#input:
5
1 1
2 1
3 1
1 1
#output:
3
2
3
4
4

题解

1.本题要求任意点可以到达的最长距离
2.分析可得首先本题的图是一个连通图,而且根据边数可得没有环
3.再者要求点出发到达的最远距离,我们不妨这样想,点出发到达的最远距离实际上
也是最远距离出发到他的距离
4.显然从直径的某一个端点出发(并进行dfs)到该点可以得到它的最远距离
5.直径两个端点,因此我们只要选出较大的就好了
6.同样我们从任意一个点出发dfs得到的最远点就是直径的某一个端点,而从直径dfs
的最远位置就是直径的另一个端点(因此本题3次dfs)

C++代码

//代码提供两种 第一种存边使用邻接表 dfs使用循环 另一种使用链式前向星 dfs
//采用递归 ;邻接矩阵过于占用内存,对于本题会爆内存(可能我写的不够好233

//1.邻接表
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int maxn=1e4+500;
int mark[maxn],MAX[maxn],MAX2[maxn],N;
vector<int> w[maxn],e[maxn];
queue<int> q;
void init(int m){
	for(int i=0;i<m;i++){
		mark[i] = 0;
		MAX[i] = 0;
		MAX2[i] = 0;
		w[i] = vector<int>(0);
		e[i] = vector<int>(0);
	}
}
int dfs(int x){
	q.push(x);
	int m=0,mid=0;
	MAX[x] = 0;
	while(!q.empty()){
		x = q.front();q.pop();
		if(mark[x]==0){
			mark[x] = 1;
			for(int i=0;i<e[x].size();i++){
				if(mark[e[x][i]]==0){
					q.push(e[x][i]);
					if(MAX[x]+w[x][i]>MAX[e[x][i]]) MAX[e[x][i]] = MAX[x]+w[x][i];
				}
			}
		}
	}
	for(int i = 0;i<N;i++){
		if(MAX[i]>mid){
			m=i;
			mid=MAX[i];
		}
		mark[i] = 0;
	}
	return m;
}
int main(){
	int a,b,m;
	while(cin>>N){
		init(N);
		for(int i=0;i<N-1;i++){
			cin>>a>>b;
			e[a-1].push_back(i+1);
			w[a-1].push_back(b);
			e[i+1].push_back(a-1);
			w[i+1].push_back(b);
		}
		m = dfs(0);
		for(int i=0;i<N;i++) {MAX[i]=0;}
		m = dfs(m);
		for(int i=0;i<N;i++){MAX2[i] = MAX[i];MAX[i] = 0;}
		dfs(m);
		for(int i=0;i<N;i++) cout<<max(MAX[i],MAX2[i])<<endl;
	}
	return 0;
}
//2.链式前向星
#include<iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int maxn=1e4+500;
int mark[maxn],MAX[maxn],MAX2[maxn],N,tot=0,head[maxn],vm;

struct Edge
{
	int u,v,w,next;
}Edges[2*maxn];

void addEdge(int u,int v,int w)
{
	Edges[tot].u=u;
	Edges[tot].v=v;
	Edges[tot].w=w;
	Edges[tot].next=head[u];
	head[u]=tot;
	tot++;
}

void init(int m){
	tot=0;vm=0;
	memset(head,-1,sizeof(head));
	for(int i=0;i<m;i++){
		mark[i] = 0;
		MAX[i] = 0;
		MAX2[i] = 0;
	}
}
void dfs(int x){
	mark[x] = 1;
	for(int i=head[x];i!=-1;i=Edges[i].next){
		if(mark[Edges[i].v]==0){
			MAX[Edges[i].v] = MAX[x]+Edges[i].w;	
			dfs(Edges[i].v);
		}
	}
}

int main(){
	int a,b,m,mid;
	while(cin>>N){
		init(N);
		for(int i=0;i<N-1;i++)
		{
			cin>>a>>b;
			addEdge(a-1,i+1,b);
			addEdge(i+1,a-1,b);
		}
		dfs(0);mid=0;//重置标记矩阵 max矩阵 vm:最远端点 
		for(int i=0;i<N;i++) { if(mid<MAX[i]) {vm=i;mid=MAX[i];} MAX[i]=0;mark[i]=0;}
		dfs(vm);mid=0;
		vm=0;
		for(int i=0;i<N;i++){if(mid<MAX[i]) {vm=i;mid=MAX[i];} MAX2[i] = MAX[i];MAX[i] = 0;mark[i]=0;}
		dfs(vm);
		for(int i=0;i<N;i++) cout<<max(MAX[i],MAX2[i])<<endl;
	}
	return 0;
}

B-戴好口罩!

题目

题目
Input&&Output:
INPUT&&OUTPUT
Sample:

#input:
100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0
#output:
4
1
1

题解

1.本题实际上是求每个小团体的人数
2.每个团体只需要有一个代表人就好,因此我们可以想到利用并查集的思想,而且可
以使用路径压缩(仅仅关注领袖)
3.并查集的合并:实际上是把某一个集合的领袖变成另一个集合的领袖,(然而下一
次修改的时候我们就需要考虑更改节点领袖的个数问题,因此小树挂大树是我们需要
的。

C++代码

#include<iostream>

using namespace std;
const int maxn = 1e5;
int st[maxn],rak[maxn];
int n,m,num;
void init(){
	for(int i = 0;i<n;i++){
		st[i] = i;
		rak[i]=1;
	}
}
int find(int i){
	if(st[i]!=i) return st[i]=find(st[i]);
	return i;	
}
bool unite(int x,int y){
	x = find(x),y = find(y);
	if(x==y) return false;
	if(x>y) swap(x,y);
	st[x] = y;
	rak[y] = rak[x]+rak[y];
	return true;
}
int main(){
	int c,cha=-1;
	while(cin>>n>>m){
		if(m == n&&m==0) break;
		init();
		while(m--){
			cha=-1;
			cin>>num;
			for(int i = 0;i<num;i++){
				cin>>c;
				if(cha!=-1) unite(c,cha);
				cha=c;
			}
		}
		cout<<rak[find(0)]<<endl;
	}
	return 0;
} 

C-掌握魔法の东东 I

题目

题目
Input&&Output:
INPUT&&OUTPUT
Sample:

#input:
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
#output:
9

题解

1.本题我们可以将油田看成一个个点,而传送门实际上就是边,黄河之水天上来实际
上也是给这些油田传水
2.因此本题实际上是一个求最小遍历图的问题或者最小生成树问题,我们将天也看做
一块油田,他与每个油田都有连边,然后我们去求最小生成树就可以了
3.Kruskal算法:将点都放上,边排序每次取最小可以增强图的连通性的边(也就是
说已连通的边不需要再加边(下述代码所采用的)
4.prim算法:本质上与kruskal类似,只不过取边然后扩充点集,直至覆盖所有点
5.两种算法各有优劣,与边 点个数有关
6.连通性判断:实际上也是一个路径压缩的并查集

C++代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1e5+500;
int m,n,root;
int u,v,w;
struct edge{
	int u,v,w;
	
	operator<(edge &x){
		return w<x.w;
	}	
};
int conn[maxn];
vector<edge> ve(0);
int find(int i) {return conn[i]==i ? i : conn[i]=find(conn[i]);}
edge c;
int main(){
	int MAX=0;
	cin>>n>>m>>root;
	for(int i=0;i<n+1;i++) conn[i]=i;
	for(int i = 0;i<m;i++){
		cin>>u>>v>>w;
		c.u=u-1,c.v=v-1,c.w=w;
		ve.push_back(c);
	}
	sort(ve.begin(),ve.end());
	for(int i=0;i<ve.size();i++){
		c=ve[i];
		find(c.u),find(c.v);
		if(conn[c.u]!=conn[c.v]){
			conn[conn[c.u]]=conn[c.v];
			if(c.w>MAX) MAX=c.w;
		}
	}
	cout<<MAX<<endl;
	return 0;
}

D-数据中心

题目

题目
Input&&Output:
INPUT&&OUTPUT
Sample:

#input:
4
5
1
1 2 3
1 3 4
1 4 5
2 3 8
3 4 2
#output:
4

题解

1.本题求流水线最小耗时,我们把它看成层实际上每层流水线同时开始,它到下一层
的最优时间取决于本层最大的时间,而整个流水线运行时,它的最优时间实际上是所
有层的最小时间(所有层的最大时间的最大值
2.我们这样去想,所有层最大时间的最大值不就是所有边的最大值么,那么我们实际
上是求一组变,它既能使流水线连通(图连通)而且边要尽可能的小,因此是一个最
小生成树问题

C++代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=1e5+500;

struct edge{
	int u,v,w;
	
	operator<(edge &x){
		return w<x.w;
	}	
};
int conn[maxn];
vector<edge> ve(0);
int find(int i) {return conn[i]==i ? i : conn[i]=find(conn[i]);}
edge c;
int main(){
	int n,w,u,v,len=0;
	cin>>n;
	for(int i=0;i<n+1;i++) conn[i]=i;
	for(int i = 0;i<n;i++){
		cin>>w;
		c.u=n,c.v=i,c.w=w;
		ve.push_back(c);
	}
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++){
			cin>>w;
			c.u=i,c.v=j,c.w=w;
			if(i<j) ve.push_back(c);
		}
	sort(ve.begin(),ve.end());
	for(int i=0;i<ve.size();i++){
		c=ve[i];
		find(c.u),find(c.v);
		if(conn[c.u]!=conn[c.v]){
			conn[conn[c.u]]=conn[c.v];
			len+=c.w;
		}
	}
	cout<<len<<endl;
	return 0;
}
发布了7 篇原创文章 · 获赞 3 · 访问量 439

猜你喜欢

转载自blog.csdn.net/TIM__one/article/details/105191752
今日推荐