数据结构上机实验解题报告

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 7.1 稀疏矩阵之差
  • 7.2 二叉树最短路径长度
  • 7.3 文字编辑
  • 7.4 方案计数

7.1 稀疏矩阵之差

7-1 稀疏矩阵之差

分数 100

作者 谷方明

单位 吉林大学

矩阵A和B都是稀疏矩阵。请计算矩阵的差A-B.如果A、B不能计算差值,输出"Illegal!"

输入格式:

矩阵的输入采用三元组表示,先A后B。对每个矩阵:

第1行,3个整数N、M、t,用空格分隔,分别表示矩阵的行数、列数和非0数据项数,10≤N、M≤50000,t≤min(N,M).

第2至t+1行,每行3个整数r、c、v,用空格分隔,表示矩阵r行c列的位置是非0数据项v, v在32位有符号整型范围内。三元组默认按行列排序。

输出格式:

矩阵A-B,采用三元组表示,默认按行列排序,非零项也在32位有符号整型范围内。

输入样例:

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

10 10 3
2 2 2
5 5 5
10 10 20
10 10 2
2 2 1
6 6 6

输出样例:

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

10 10 4
2 2 1
5 5 5
6 6 -6
10 10 20

代码长度限制

16 KB

时间限制

100 ms

内存限制

10 MB

思路:

1.将稀疏矩阵按行列顺序存储

先存入然后使用sort排序保证行列序

2.将两个稀疏矩阵相减

因为行列小的要先存入,我这里认为如果一个矩阵已经合并结束,也不退出循环,而是将它代表结点的值赋值0,行列复制为无穷

在计算时若出现相减等于0的val值应避免录入

余下思路在注释体现

参考代码:

#include <iostream>
#include <vector>
#include <algorithm>
typedef struct {
	int row;//行
	int col;//列
	int val;
}Matrix;
bool com(Matrix a, Matrix b) {
	if (a.row != b.row) { return a.row < b.row; }
	else if (a.col != b.col) { return a.col < b.col; }
}
using namespace std;
vector<Matrix> ans;
int min(int m, int n) {
	if (m > n) {
		return n;
	}
	else {
		return m;
	}
}
bool Isalessb(Matrix A, Matrix B) {//a是否小于b?
	if (A.row != B.row) { return A.row < B.row; }
	if (A.col != B.col) { return A.col < B.col; }
	return false;
}
bool Isaequalb(Matrix A, Matrix B) {//a是否等于b?
	if (A.row == B.row && A.col == B.col) {
		return true;
	}
	return false;
}
Matrix* newjuzhen(int M, int N, int t) {
	int row = 0, col = 0, val = 0;
	int countA = 0;
	Matrix* A = new Matrix[min(M, N)];
	for (int i = 0; i < t; i++) {
		scanf("%d%d%d", &row, &col, &val);
		A[i].col = col;
		A[i].row = row;
		A[i].val = val;
	}
	sort(A, A + t, com);
	return A;
}
void jianjuzhen(Matrix* A, Matrix* B, int t, int t2) {
	int i = 0, j = 0, k = 0;
	Matrix cupa, cupb;//对AB每个项的两个临时节点 节点相减后存入容器ans
	while (i != t || j != t2) {//ij全部到头才停止循环
		if (i != t) {
			cupa.row = A[i].row;
			cupa.col = A[i].col;
			cupa.val = A[i].val;
		}
		else {
			cupa.row = 10000000;
			cupa.col = 10000000;
			cupa.val = 0;
		}
		if (j != t2) {
			cupb.row = B[j].row;
			cupb.col = B[j].col;
			cupb.val = B[j].val;
		}
		else {
			cupb.row = 10000000;
			cupb.col = 10000000;
			cupb.val = 0;
		}
		if (Isalessb(cupa, cupb)) {//行列小的先录入保证ans的顺序
			ans.push_back(cupa);
			i++;
		}
		else if (Isaequalb(cupa, cupb)) {//行列值相等时 ab的val相减进行计算
			if (cupa.val - cupb.val != 0) {
				Matrix cuppp;
				ans.push_back(Matrix());
				ans.back().val = cupa.val - cupb.val;
				ans.back().row = cupa.row;
				ans.back().col = cupa.col;
			}
			i++; j++;//val相减等于0的情况由三元组的性质就不录入 ij直接进一位
		}
		else {
			cupb.val = -cupb.val;//没有a的干扰 b直接录入的情况 由于b是减数所以val录入-b.val
			ans.push_back(cupb);
			j++;
		}
	}
}
int main() {
	int M = 0, N = 0, t = 0;
	int M2 = 0, N2 = 0, t2 = 0;
	scanf("%d%d%d", &M, &N, &t);
	Matrix* A = newjuzhen(M, N, t);
	scanf("%d%d%d", &M2, &N2, &t2);
	if (M != M2 || N != N2) {
		printf("Illegal!");
	}
	else {
		Matrix* B = newjuzhen(M2, N2, t2);
		jianjuzhen(A, B, t, t2);

		printf("%d %d %d\n", M, N, ans.size());
		for (int i = 0; i < ans.size(); i++) {
			printf("%d %d %d", ans[i].row, ans[i].col, ans[i].val);
			if (i < ans.size() - 1) {
				printf("\n");
			}
		}
	}
}

7-2 二叉树最短路径长度

给定一棵二叉树T,每个结点赋一个权值。计算从根结点到所有结点的最短路径长度。路径长度定义为:路径上的每个顶点的权值和。

输入格式:

第1行,1个整数n,表示二叉树T的结点数,结点编号1..n,1≤n≤20000。

第2行,n个整数,空格分隔,表示T的先根序列,序列中结点用编号表示。

第3行,n个整数,空格分隔,表示T的中根序列,序列中结点用编号表示。

第4行,n个整数Wi,空格分隔,表示T中结点的权值,-10000≤Wi≤10000,1≤i≤n。

输出格式:

1行,n个整数,表示根结点到其它所有结点的最短路径长度。

输入样例:

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

4
1 2 4 3
4 2 1 3
1 -1 2 3

输出样例:

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

1 0 3 3

代码长度限制

16 KB

时间限制

1000 ms

内存限制

10 MB

思路:

先根序列中根序列建树比较难 然后dfs求最短路径

参考代码:

#include<stdio.h>
int first[20010];
int mid[20010];
int left[20010];
int right[20010];
int value[20010];
int cost[20010];
void buildt(int , int , int );
void dfs(int root) {
	if (!root) return;
	cost[root] += value[root];
	cost[left[root]] += cost[root];
	cost[right[root]] += cost[root];
	dfs(left[root]);
	dfs(right[root]);
}
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &first[i]);
	}
	for (int i = 1; i <= n; i++) {
		scanf("%d", &mid[i]);
	}
	buildt(1, 1, n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &value[i]);
	}
	for (int i = 1; i <= n; i++) {
		cost[i] = 0;
	}
	int root = first[1];
	dfs(root);
	for (int i = 1; i < n; i++)
		printf("%d ", cost[i]);
	printf("%d", cost[n]);
}
void buildt(int d, int s2, int e2) {
	int i = s2;
	while (first[d] != mid[i]) {
		i++;
	}
	if (s2 == e2) {
		left[first[d]] = right[first[d]] = 0;
	}
	else if (i == e2) {
		right[first[d]] = 0;
		left[first[d]] = first[d + 1];
		buildt(d + 1, s2, i - 1);//zuo
	}
	else if (i == s2) {
		left[first[d]] = 0;
		right[first[d]] = first[d + 1];
		buildt(d + 1, i + 1, e2);//you
	}
	else {
		right[first[d]] = first[d + i - s2 + 1];
		left[first[d]] = first[d + 1];
		buildt(d + i - s2 + 1, i + 1, e2);//you
		buildt(d + 1, s2, i - 1);//zuo
	}

}

7-3 文字编辑

一篇文章由n个汉字构成,汉字从前到后依次编号为1,2,……,n。
有四种操作:

A i j表示把编号为i的汉字移动编号为j的汉字之前;

B i j表示把编号为i的汉字移动编号为j的汉字之后;

Q 0 i为询问编号为i的汉字之前的汉字的编号;

Q 1 i为询问编号为i的汉字之后的汉字的编号。

规定:1号汉字之前是n号汉字,n号汉字之后是1号汉字。

输入格式:

第1行,1个整数T,表示有T组测试数据, 1≤T≤9999.

随后的每一组测试数据中,第1行两个整数n和m,用空格分隔,分别代表汉字数和操作数,2≤n≤9999,1≤m≤9999;第2至m+1行,每行包含3个常量s、i和j,用空格分隔,s代表操作的类型,若s为A或B,则i和j表示汉字的编号,若s为Q,i代表0或1,j代表汉字的编号。

输出格式:

若干行,每行1个整数,对应每个询问的结果汉字编号。

输入样例:

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

1 
9999 4 
B 1 2  
A 3 9999
Q 1 1
Q 0 3

输出样例:

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

4
9998

代码长度限制

16 KB

时间限制

1000 ms

内存限制

2 MB

思路:

用next last数组初始化为对应位置然后按要求修改 查询即可

参考代码:

#include<stdio.h>
int read() {
	int f = 0, x = 0;
	char ch = getchar();
	while (!(ch >= '0' && ch <= '9'))	f |= (ch == '-'), ch = getchar();
	while (ch >= '0' && ch <= '9')	x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
void input() {
	int t = read();
	while (t--) {
		int n = read(), m = read();
		int next[100001], last[100001];
		for (int i = 1; i <= n; ++i)	next[i] = i + 1, last[i] = i - 1;//next和last储存前移和后移一位的数组 并循环进位
		next[n] = 1;
		last[1] = n;
		for (int i = 1; i <= m; ++i) {
			char ch;
			scanf("%c", &ch);
			int a = read(), b = read();
			switch (ch) {
			case 'A': {
				if (last[b] == a)	continue;
				next[last[a]] = next[a];
				last[next[a]] = last[a];
				last[a] = last[b];
				last[b] = a;
				next[last[a]] = a;
				next[a] = b; break;
			}
			case 'B': {
				if (next[b] == a)	continue;
				next[last[a]] = next[a];
				last[next[a]] = last[a];
				next[a] = next[b];
				next[b] = a;
				last[next[a]] = a;
				last[a] = b; break;
			}
			case 'Q': {
				if (a)	printf("%d\n", next[b]);
				if (!a)	printf("%d\n", last[b]); break;
			}
			}
		}
	}
}
int main() {
	input();
	return 0;
}

7-4 方案计数

组装一个产品需要 n 个零件。生产每个零件都需花费一定的时间。零件的生产可以并行进行。有些零件的生产有先后关系,只有一个零件的之前的所有零件都生产完毕,才能开始生产这个零件。如何合理安排工序,才能在最少的时间内完成所有零件的生产。在保证最少时间情况下,关键方案有多少种,关键方案是指从生产开始时间到结束时间的一个零件生产序列,序列中相邻两个零件的关系属于事先给出的零件间先后关系的集合,序列中的每一个零件的生产都不能延期。

输入格式:

第1行,2个整数n和m,用空格分隔,分别表示零件数和关系数,零件编号1..n,1≤n≤10000, 0≤m≤100000 。

第2行,n个整数Ti,用空格分隔,表示零件i的生产时间,1≤i≤n,1≤Ti≤100 。

第3到m+2行,每行两个整数i和j,用空格分隔,表示零件i要在零件j之前生产。

输出格式:

第1行,1个整数,完成生产的最少时间。

第2行,1个整数,关键方案数,最多100位。

如果生产不能完成,只输出1行,包含1个整数0.

输入样例:

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

4 4
1 2 2 1
1 2
1 3
2 4
3 4

输出样例:

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

4
2

代码长度限制

16 KB

时间限制

200 ms

内存限制

64 MB

思路:

拓补排序求关键路径 AOEAOV什么的 用高精度求路径数

我不会 这里借用大佬的代码

参考代码:

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define f(i,a,b) for(int i=a;i<=b;i++)
#define _f(i,a,b) for(int i=a;i>=b;i--)
const int maxn = 1e4 + 5;
string add(string a, string b) {
vector<char> cup;
int tmp = 0;
int carry = 0;
char t1 = '0', t2 = '0';
while (!a.empty() || !b.empty()) {
if (!a.empty())
{
t1 = a.back();a.pop_back();
}
else t1 = '0';
if (!b.empty())
{
t2 = b.back();b.pop_back();
}
else t2 = '0';
tmp = t1 + t2 - 2 * '0' + carry;
if (tmp < 10) {
cup.push_back(tmp + '0');
carry = 0;
}
else {
cup.push_back(tmp % 10 + '0');
carry = 1;
}
}
if (carry == 1) {
cup.push_back('1');
}
string ans(cup.rbegin(), cup.rend());
return ans;
}
string path[maxn];
vector<int>edge[maxn];
int cost[maxn], degree[maxn], ecost[maxn];
int n, m;
queue<int>q;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> m;
f(i, 1, n)cin >> ecost[i];
while (m--) {
int u, v;
cin >> v >> u;
edge[u].push_back(v);
degree[v]++;
}
f(i, 1, n) {
if (degree[i]==0) {
q.push(i);
cost[i] = ecost[i];
path[i] = "1";
}
}
f(i, 1, n) {
if (q.empty()) {
cout << "0";
return 0;
}
int u = q.front();q.pop();
while (!edge[u].empty()) {
int v = edge[u].back();
edge[u].pop_back();
degree[v]--;
if (cost[v] < cost[u] + ecost[v]) {
cost[v] = cost[u] + ecost[v];
path[v] = path[u];
}
else
if (cost[v] == cost[u] + ecost[v])
path[v] = add(path[v], path[u]);
if (!degree[v])q.push(v);
}
}
string ans ("0");
int lenth = -1;
f(i, 1, n) {
if (lenth < cost[i]) {
ans = path[i];
lenth = cost[i];
}
else if (lenth == cost[i]) {
ans = add(ans, path[i]);
}
}
cout << lenth << "\n";
cout << ans;
return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
好的,以下是骑士跳棋问题的实验报告: 一、题目解析: 骑士跳棋是一种经典的棋类游戏,游戏中玩家需要将棋盘上的骑士移动到目标位置。骑士的移动规则是:每次可以向上、下、左、右、斜向上或者斜向下跳两格再向左或者向右跳一格。本实验旨在通过编程实现骑士跳棋问题的求解,并且通过不同的算法对其进行优化。 二、解题思路: 骑士跳棋问题可以通过回溯算法和启发式搜索算法进行求解。 回溯算法:回溯算法是一种通过穷举所有可能的解来求解问题的方法。在骑士跳棋问题中,我们可以从起始位置开始,依次尝试所有可能的移动方式,直到找到一种移动方式能够到达目标位置。如果无法到达目标位置,则回溯到上一步并尝试其他移动方式。 启发式搜索算法:启发式搜索算法是一种通过启发函数来指导搜索方向的方法。在骑士跳棋问题中,我们可以定义一种启发函数来评估当前位置到目标位置的距离,并将距离最短的位置作为下一步的移动位置。这样可以大大减少搜索的时间和空间复杂度。 三、解题步骤: 1. 定义骑士跳棋问题的状态表示方式,包括起始位置和目标位置。 2. 实现回溯算法或者启发式搜索算法来求解骑士跳棋问题。 3. 对算法进行优化,例如使用剪枝等技术来减少搜索的时间和空间复杂度。 4. 对算法进行测试,并比较不同算法的效率和精度。 四、实验结果: 我们使用Python语言实现了骑士跳棋问题的求解,并且使用了回溯算法和启发式搜索算法进行优化。在测试中,我们发现启发式搜索算法可以大大缩短求解时间,并且得到的解也更加精确。同时,我们还使用了剪枝技术来进一步优化搜索算法,得到了更好的效果。 具体代码: 以下是使用回溯算法求解骑士跳棋问题的Python代码: ``` def knight_tour(n, path, u, limit): u.visited = True path.append(u) if n < limit: nbr_list = list(u.get_connections()) i = 0 done = False while i < len(nbr_list) and not done: if not nbr_list[i].visited: done = knight_tour(n+1, path, nbr_list[i], limit) i += 1 if not done: path.pop() u.visited = False else: done = True return done ``` 以下是使用启发式搜索算法求解骑士跳棋问题的Python代码: ``` def knight_tour(n, path, u, limit): u.visited = True path.append(u) if n < limit: nbr_list = list(u.get_connections()) nbr_list.sort(key=lambda x: x.get_heuristic(u)) i = 0 done = False while i < len(nbr_list) and not done: if not nbr_list[i].visited: done = knight_tour(n+1, path, nbr_list[i], limit) i += 1 if not done: path.pop() u.visited = False else: done = True return done ``` 其中,get_heuristic方法用于计算当前位置到目标位置的距离。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值