JLU数据结构上机实验四

7-1 连通分量 (100 分)

作者 :谷方明

单位 :吉林大学

代码长度限制 :16 KB

时间限制 :200 ms

内存限制 :10 MB

无向图 G 有 n 个顶点和 m 条边。求 G 的连通分量的数目。

输入格式:

第1行,2个整数n和m,用空格分隔,分别表示顶点数和边数, 1≤n≤50000, 1≤m≤100000.

第2到m+1行,每行两个整数u和v,用空格分隔,表示顶点u到顶点v有一条边,u和v是顶点编号,1≤u,v≤n.

输出格式:

1行,1个整数,表示所求连通分量的数目。

输入样例:

在这里给出一组输入。例如:

6 5
1 3
1 2
2 3
4 5
5 6

输出样例:

在这里给出相应的输出。例如:

2

我使用了vector保存路径,下标表示路径出发的结点,vector中存的值表示路径终止的结点。正如前面所说的,这样保存的是有向图,无向图应该保存一条由起点到终点和由终点到起点两条有向边。

#include<bits/stdc++.h>
using namespace std;
/*文件输入输出*/ 
const int MAXN=1e5+5;
int v[MAXN];
vector<int>q[MAXN];
void dfs(int a){
	//cout<<a<<endl;
	if(v[a]==1) return;
	v[a]=1;
	for(int i=0;i<q[a].size();i++){
		dfs(q[a][i]);
	}
}
int main(){
	int n;
    long long m;
	long long count=0;
	scanf("%d%lld",&n,&m);
	for(long long i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		q[u].push_back(v);
		q[v].push_back(u);
	}
	for(int i=1;i<=n;i++) v[i]=0;
	for(int i=1;i<=n;i++){
		if(v[i]==0){
			dfs(i); count++;
		}
	}
    printf("%lld\n",count);
	return 0;
}

7-2 整数拆分 (100 分)

作者 :谷方明

单位 :吉林大学

代码长度限制 :16 KB

时间限制 :100 ms

内存限制 :1 MB

整数拆分是一个古老又有趣的问题。请给出将正整数 n 拆分成 k 个正整数的所有不重复方案。例如,将 5 拆分成 2 个正整数的不重复方案,有如下2组:(1,4)和(2,3)。注意(1,4) 和(4,1)被视为同一方案。每种方案按递增序输出,所有方案按方案递增序输出。

输入格式:

1行,2个整数n和k,用空格分隔, 1≤k≤n≤50.

输出格式:

若干行,每行一个拆分方案,方案中的数用空格分隔。

最后一行,给出不同拆分方案的总数。

输入样例:

在这里给出一组输入。例如:

5 2

输出样例:

在这里给出相应的输出。例如:

1 4
2 3
2
  •  第二题也是一道不太难的题呢!(结果考试的时候四道题一道没做出来QAQ)就是使用DFS+回溯法。容器有用过vector和数组,数组可以直接覆盖原来的值,实现隐形的回溯,所以最后选用了数组。

这道题在考场上一直出的问题是方案总数是对的,但是每个方案中数的大小会小一个1。是递归调用函数和赋值两步的顺序出了问题。(我到底是多么不清醒才会犯这种错误啊)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
long long q[MAXN];
int n,k;
long long Count=0;
void B(int pre,int a,int b) { //已经放进了几个数,剩余和
	//cout<<"f"<<a<<b<<endl;
	//system("pause");
	if(a==k) {
		if(b==0) {
			for(int i=0; i<k; i++) {
				printf("%lld",q[i]);
				printf("%c",i==k-1?'\n':' ');
			}
			Count++;
		}
		return;
	}
	if(b<=0) return;
	for(int i=pre; i<=b; i++) {
        q[a]=i;
		B(i,a+1,b-i);
	}
}

int main() {
	scanf("%d%d",&n,&k);
	//cout<<n<<k<<endl;
	B(1,0,n);
	printf("%lld\n",Count);
}

7-3 数字变换 (100 分)

利用变换规则,一个数可以变换成另一个数。变换规则如下:(1)x 变为x+1;(2)x 变为2x;(3)x 变为 x-1。给定两个数x 和 y,至少经过几步变换能让 x 变换成 y.

输入格式:

1行,2个整数x和y,用空格分隔, 1≤x,y≤100000.

输出格式:

第1行,1个整数s,表示变换的最小步数。

第2行,s个数,用空格分隔,表示最少变换时每步变换的结果。规则使用优先级顺序: (1),(2),(3)。

输入样例:

在这里给出一组输入。例如:

2 14

输出样例:

在这里给出相应的输出。例如:

4
3 6 7 14
  •  这道题我一开始用的是DFS(因为我只会DFS),但是后来发现这是个无限图问题,DFS能找到无数个解,但是无法找出最优解。听了其他同学的讲解,才知道应该使用BFS求最优解。

用了BFS后,有两个点会内存超限,通过看其他同学的题解,我注意到了题目中给出的x,y的范围,根据有限的三种变换可知,超出这个范围的数应该不会出现在最优解中,所以对这个部分进行了限制。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e7+10;
int v[MAXN];
int step[MAXN];
int stepnum[MAXN];
int result[MAXN];
queue<int>q;
int x,y;
void bfs(){
	while(!q.empty()){
		int u=q.front();
		if(u==y) return;
		q.pop();
		if(!v[u+1]&&u+1<=1e5&&u+1>=1){
			v[u+1]=1;
			step[u+1]=u;
			stepnum[u+1]=stepnum[u]+1;
           // cout<<u+1<<" "<<stepnum[u+1]<<endl;
			q.push(u+1);
		}
		if(!v[2*u]&&2*u<=1e5&&2*u>=1){
			v[2*u]=1;
			step[u*2]=u;
			stepnum[2*u]=stepnum[u]+1;
            //cout<<2*u<<" "<<stepnum[2*u]<<endl;
			q.push(2*u);
		}
		if(!v[u-1]&&u-1<=1e5&&u-1>=1){
			v[u-1]=1;
			step[u-1]=u;
			stepnum[u-1]=stepnum[u]+1;
            //cout<<u-1<<" "<<stepnum[u-1]<<endl;
			q.push(u-1);
		}
	}
}
int main(){
	scanf("%d%d",&x,&y);
	v[x]=1; step[x]=0; stepnum[x]=0;
	q.push(x);
	bfs();
	int tmp=y;
	int count=0;
	while(tmp){
		result[count++]=tmp;
       // cout<<tmp<<" "<<stepnum[tmp]<<endl;
		tmp=step[tmp];
	}
	printf("%d\n",stepnum[y]);
	for(int i=count-2;i>=0;i--){
		printf("%d",result[i]);
		printf("%c",i==0?'\n':' ');
	}
	return 0;
}

7-3 数字变换 (100 分)

五一要到了,来一场说走就走的旅行吧。当然,要关注旅行费用。由于从事计算机专业,你很容易就收集到一些城市之间的交通方式及相关费用。将所有城市编号为1到n,你出发的城市编号是s。你想知道,到其它城市的最小费用分别是多少。如果可能,你想途中多旅行一些城市,在最小费用情况下,到各个城市的途中最多能经过多少城市。

输入格式:

第1行,3个整数n、m、s,用空格分隔,分别表示城市数、交通方式总数、出发城市编号, 1≤s≤n≤10000, 1≤m≤100000 。

第2到m+1行,每行三个整数u、v和w,用空格分隔,表示城市u和城市v的一种双向交通方式费用为w , 1≤w≤10000。

输出格式:

第1行,若干个整数Pi,用空格分隔,Pi表示s能到达的城市i的最小费用,1≤i≤n,按城市号递增顺序。

第2行,若干个整数Ci,Ci表示在最小费用情况下,s到城市i的最多经过的城市数,1≤i≤n,按城市号递增顺序。

输入样例:

在这里给出一组输入。例如:

5 5 1
1 2 2
1 4 5
2 3 4
3 5 7
4 5 8

输出样例:

在这里给出相应的输出。例如:

0 2 6 5 13
0 1 2 1 3

 一个简单的无向权图求最短路径的题。我使用了Dijkstra算法,一开始错误的原因和第一题一样,把无向图存成了有向图。

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
int visited[MAXN];
vector<int>q[MAXN];
vector<int>cost[MAXN];
int dist[MAXN];//路径长度
int path[MAXN];//前一个结点
int c[MAXN];
int n,m,s;
void Dijkstra(int head) {
	//	cout<<"Dijkstra"<<endl;
	for (int i = 1; i <= n; i++) {
		path[i] = -1;
		dist[i] = MAXN;
		visited[i] = 0;
		c[i]=-1;
	}
	dist[head] = 0;
	visited[head] = 1;
	c[head]=0;
	int u = head;
	for (int i = 1; i < n; i++) {
		//cout<<u<<endl;
		//cout<<q[u].size()<<endl;
		for (int j = 0; j < q[u].size(); j++) {
			int k = q[u][j];
			if (!visited[k] && dist[u] + cost[u][j] < dist[k]) {
				dist[k] = dist[u] + cost[u][j];
				path[k] = u;
				c[k]=c[u]+1;

			}
			if(!visited[k] && dist[u] + cost[u][j] == dist[k]){
				if(c[u]+1>c[k]) {
					c[k]=c[u]+1;
					path[k]=u;
				}
			}
		}
	int ldist = MAXN;
	for (int j = 1; j <= n; j++) {
			if (dist[j] < ldist&&visited[j] == 0) {
				ldist = dist[j];
				u = j;
			}
		}
		visited[u] = 1;
	}
	for (int k = 1; k <= n; k++) {
		printf("%d",dist[k]);
		printf("%c",k==n?'\n':' ');
	}
	for (int k = 1; k <= n; k++) {
		printf("%d",c[k]);
		printf("%c",k==n?'\n':' ');
	}
}
int main() {
	//int n,m,k;
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1; i<=m; i++) {
		int u,v,d;
		scanf("%d%d%d",&u,&v,&d);
		//cout<<u<<v<<d;
		q[u].push_back(v);
		cost[u].push_back(d);
		q[v].push_back(u);
		cost[v].push_back(d);
	}
	Dijkstra(s);
}

 

第一次实验: 题目1 单链表相关算法的实验验证。 [实验目的] 验证单链表及其上的基本操作。 [实验内容及要求] 1、 定义单链表类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建单链表; 2)插入操作:分别在当前结点后、表头、表尾插入值为x的结点; 3)删除操作:分别删除表头结点、表尾结点和当前结点的后继结点; 4)存取操作:分别存取当前结点的值和单链表中第k个结点的值; 5)查找操作:查找值为x的元素在单链表中的位置(下标)。 题目2 分别给出堆栈、队列相关算法的实验验证。 [实验目的] 验证堆栈、队列及其上的基本操作。 [实验内容及要求](以队列为例) 1、 定义队列类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建队列; 2)插入操作:向队尾插入值为x的元素; 3)删除操作:删除队首元素; 4)存取操作:读取队首元素。 第二次实验 题目1 二叉树相关算法的实验验证。 [实验目的] 验证二叉树的链接存储结构及其上的基本操作。 [实验内容及要求] 1、 定义链接存储的二叉树类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建一棵二叉树,并对其初始化; 2)先根、中根、后根遍历二叉树(递归算法); 3)在二叉树中搜索给定结点的父结点; 4)搜索二叉树中符合数据域条件的结点; 5)从二叉树中删除给定结点及其左右子树。 题目2 树和森林的遍历算法的实验验证。 [实验目的] 验证树和森林的遍历算法。 [实验内容及要求] 1、 定义左儿子—右兄弟链接存储的树类和森林类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建树和森林; 2)树和森林的先根遍历的递归和迭代算法; 3)树和森林的后根遍历的递归和迭代算法; 4)树和森林的层次遍历算法。 题目3 二叉查找树的验证实验。 [实验目的] 验证二叉查找树及其相关操作。 [实验内容及要求] 1、 定义二叉查找树的类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)实现二叉查找树结构; 2) 实现二叉查找树的查找、插入和删除等算法; 第三次实验 题目1 邻接表存储的图相关算法的实验验证。 [实验目的] 验证邻接表存的图及其上的基本操作。 [实验内容及要求] 1、 定义邻接表存储的图类。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建一个邻接表存储的图; 2)返回图中指定边的权值; 3)返回图中某顶点的第一个邻接顶点; 4)返回图中某顶点关于另一个顶点的下一个邻接顶点的序号; 5)插入操作:向图中插入一个顶点,插入一条边; 6)删除操作:从图中删除一个顶点,删除一条边。 题目2 图的遍历算法的实验验证。 [实验目的] 验证图的遍历算法。 [实验内容及要求] 1、 定义邻接表存储的图。 2、 实验验证如下算法的正确性、各种功能及指标: 1)创建一个图; 2)图的深度优先遍历的递归算法; 3)图的深度优先遍历的迭代算法; 4)图的广度优先遍历算法。 第实验 折半插入排序,堆排序,快速排序 请阅读说明文档
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值