JLU数据结构上机实验6

7-1 高精度数加法

原题呈现:
在这里插入图片描述

解题思路 : 1.毫无疑问板子是两个数的高精度加法,用到倒序写入、模拟竖式加法等技巧,不再赘述。2.由两个数扩展到N个数只要加一重循环,且在循环体最后把每次加和的结果作为下一次的一个加数即可。

以下是根据这一思路写出的代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1000010; 
char s1[maxn], s2[maxn];
int a[maxn], b[maxn], c[maxn];

int main() {
    int num, len;
    scanf("%d\n", &num);
    scanf("%s\n", s1);
    num--;
    while (num--) {
        if (num == 1)
            break;
        scanf("%s\n",s2);//读入字符串b
        //将字符串s1写入到数组a中
        int len1 = strlen(s1);
        for (int i = 0; i < len1; i++) {
            //倒序写入
            a[i] = s1[len1 - i - 1] - '0';
        }
        //将字符串s2写入到数组b中
        int len2 = strlen(s2);
        for (int i = 0; i < len2; i++) {
            //倒序写入
            b[i] = s2[len2 - i - 1] - '0';
        }

        //模拟竖式加法
        int jw = 0;//进位
        len = max(len1, len2) + 1;//注意因为最高位可能出现进位
        for (int i = 0; i < len; i++) {
            c[i] = a[i] + b[i] + jw;//当前加数A位数据+加数B位位数据+上一位的进位
            jw = c[i] / 10;//本次加法是否存在进位
            c[i] %= 10;//只能保存 0 ~ 9 的数据
        }

        //删除前导零
        for (int i = len - 1; i >= 0; i--) {
            //因为我们是从索引 0 开始,所以最高位是保存在 len-1
            if (0 == c[i] && len > 1) {
                //注意要有 len>1 这个条件。考虑特殊情况,加法结果为 00,我们实际要输出 0。
                len--;
            }
            else {
                //第一个不是零的最高位,结束删除
                break;
            }
        }
        //把s1更新为之前所有数的和,用char[]储存
        for (int i = 0; i < len; i++) {
            s1[len-1-i] = c[i] + '0';
        }
        //清空数组a、b
        for (int i = 0; i < len; i++) {
            a[i] = 0;
            b[i] = 0;
        }
    }

    //特判:只有一个加数
    if (num == -1) {
        int i = 0;
        while (s1[i] == '0')
            i++;
        printf("%s\n", s1 + i);
    }

    //逆序打印输出
    else {
        for (int i = len - 1; i >= 0; i--) {
            printf("%d", c[i]);
        }
        printf("\n");
    }
    
}

解后反思: 1.注意特判N=1时的情形。2.注意每次循环后清空数组,以免发生奇奇怪怪的错误。

7-2 二叉树加权距离

原题呈现:
在这里插入图片描述

解题思路 : 一开始使用了LCA的板子,结果空间超了。再读题发现该题只要查询一次,套用LCA的板子时空开销太大。故直接建树,并保存每个节点的父亲节点和深度,最后找出两个节点的公共父亲,再进行一系列简单操作即可。

以下是根据这一思路写出的代码:

#include<bits/stdc++.h>
using namespace std;
struct tree {
	int num;
	int depth;
	struct tree* father;
	struct tree* left;
	struct tree* right;
};
tree t[100005];

int main()
{
	int num;
	scanf("%d", &num);
	for (int i = 1; i < num; i++)
	{
		int f, s;
		scanf("%d %d", &f, &s);
		if (t[f].left == NULL) 
			t[f].left = &t[s];
		else 
			t[f].right = &t[s];
		t[s].father = &t[f];
		t[s].depth = t[f].depth + 1;
	}
	int u, v;
	scanf("%d %d", &u, &v);
	tree* f1 = &t[u], * f2 = &t[v];
	while (f1 != f2)
	{
		if (f1->depth > f2->depth)
			f1 = f1->father;
		else
			f2 = f2->father;
	}
	int	res = (t[u].depth - f1->depth) * 3 + (t[v].depth - f1->depth) * 2;
	printf("%d", res);
}

也可以用静态形式储存树,代码如下:

#include<bits/stdc++.h>
using namespace std;

int depth[100005],father[100005], l[100005], r[100005];

int main()
{
	int num;
	scanf("%d", &num);
	for (int i = 1; i < num; i++)
	{
		int f, s;
		scanf("%d %d", &f, &s);
		if (l[f] == 0)
			l[f] = s;
		else
			r[f] = s;
		father[s] = f;
		depth[s] = depth[f] + 1;
	}
	int u, v;
	scanf("%d %d", &u, &v);
	int f1 = u, f2 = v;
	while (f1 != f2)
	{
		if (depth[f1] > depth[f2])
			f1 = father[f1];
		else
			f2 = father[f2];
	}
	int	res = (depth[u] - depth[f1]) * 3 + (depth[v] - depth[f1]) * 2;
	printf("%d", res);
}

解后反思: 一般有多组查询时可以用打表或者具有类似思想的算法;如果只有一次查询,则直接模拟/暴力可能就能达到题目要求,且能节省时空开销。

7-3 修轻轨

原题呈现:
在这里插入图片描述

解题思路:
1.因为题目要求最小施工天数,自然想到先取施工天数少的,可以利用sort对节点排序;又因为一共只有n段路,又有n支队伍可同时施工,故一支队伍只要修一段其他队伍不修的,就可以完成任务,故不用考虑n的限制(此处比较抽象,只可意会)。
2.首先考虑用flag数组标记每一段路是否打通,并用一个变量标记已经打通的路的段数,当该变量的值等于n时,终止循环。但是,这种方法时间复杂度过高,因而考虑用并查集优化:当find(n) = find(1)时,表示从1~n均被打通。

以下是根据这一思路写出的代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;

struct edge
{
	int f, t, c;
}e[100010];
bool cmp(const edge& a, const edge& b) {
	return a.c < b.c;
}
int f[maxn];//并查集

int find(int x)
{
	if (f[x] == x)
		return x;
	return f[x] = find(f[x]);
}
void merge(int m, int n)
{
	int x = find(m), y = find(n);
	if (x != y){
		f[x] = y;
	}
}

int main()
{
	int n, m, ans=0;
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; ++i)
		f[i] = i;
	for (int i = 1, u, v, w; i <= m; ++i)
		scanf("%d %d %d", &e[i].f, &e[i].t, &e[i].c);
	sort(e + 1, e + m + 1, cmp);
	int count = 0;
	for (int i = 1; i <= m && count != n - 1; ++i)
	{
		if (find(e[i].f) != find(e[i].t)){
			count++;
			ans = e[i].c;
			merge(e[i].f, e[i].t);
		}
		if (find(1) == find(n))
			break;
	}
	printf("%d\n", ans);
}

解后反思: 注意并查集适用情景的多样化。

7-4 数据结构设计I

原题呈现:
在这里插入图片描述

解题思路: 因为可以从表的一端插入,另一端删除,故考虑用双端队列;因为有取反操作,故同时维护maxn,minn两个双端队列,并用flag记录取反的次数;此外,题目中还要维护一个区间,故用cnt标明队尾(插入位置),del标明队首(删除位置)。

以下是根据这一思路写出的代码:

#include<bits/stdc++.h>
using namespace std;
const int mmax = 1000010;

deque<int> maxn, minn;
int m, n, flag, a[mmax], cnt, del;

int main()
{
	scanf("%d", &m);
	char op;
	while (m--)
	{
		scanf("\n%c", &op);
		if (op == 'I') {
			cnt++;
			scanf("%d", &a[cnt]);
			if (pow(-1, flag) == -1)
				a[cnt] = -a[cnt];
			int tmp = a[cnt];

			while (!maxn.empty() && a[maxn.back()] >= tmp)
				maxn.pop_back();
			maxn.push_back(cnt);
			while (!minn.empty() && a[minn.back()] <= tmp)
				minn.pop_back();
			minn.push_back(cnt);
		}
		else if (op == 'D') {
			if (del < cnt) {
				del++;
				while (!maxn.empty() && maxn.front() <= del)
					maxn.pop_front();
				while (!minn.empty() && minn.front() <= del)
					minn.pop_front();
			}

		}
		else if (op == 'R') {
			flag++;
		}		
		else if (op == 'M') {
			if (del < cnt) {
				if (pow(-1, flag) == -1) {
					printf("%d\n", -a[maxn.front()]);
				}
				else {
					printf("%d\n", a[minn.front()]);
				}
			}
		}
	}

}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值