JLU数据结构第六次上机实验解题报告

本文探讨了在数据结构学习中处理高精度数的加法问题,通过数组模拟竖式计算实现。此外,介绍了二叉树中定义的特殊路径权重计算,以及在动态数据结构中支持插入、删除、取反和取最大值操作的高效方法。这些算法涉及到了图论、数据结构和数值计算等多个方面。
摘要由CSDN通过智能技术生成

T1

题目:

高精度数是指大大超出了标准数据类型能表示的范围的数,例如10000位整数。很多计算问题的结果都很大,因此,高精度数极其重要。

一般使用一个数组来存储高精度数的所有数位,数组中的每个元素存储该高精度数的1位数字或多位数字。 请尝试计算:N个高精度数的加和。这个任务对于在学习数据结构的你来说应该是小菜一碟。 。

INPUT:

第1行,1个整数N,表示高精度整数的个数。

第2至N+1行,每行1个高精度整数x。

OUTPUT:

1行,1个高精度整数,表示输入的N个高精度数的加和。

Sample

INPUT

3
12345678910
12345678910
12345678910

OUTPUT

37037036730

Time Limit

100ms

Memory Limit

1MB

Data range

1≤N≤10000 x最多100位

Solution:

数组模拟竖式计算即可

赛时AC代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 1005;

struct Add{
	int a[N];
	Add(){memset(a,0,sizeof a);}
	void read(){
		memset(a,0,sizeof a);
		char s[N];
		scanf("%s",s+1);
		a[0] = strlen(s+1);
		for(int i=a[0];i>=1;i--)
			a[a[0]-i+1] = s[i] - '0';
	}
	void print(){
		for(int i=a[0];i>=1;i--)
			printf("%d",a[i]);
	}
	Add operator +(const Add &b){
		Add res;res.a[0] = max(a[0],b.a[0]);
		for(int i=1;i<=res.a[0];i++)
			res.a[i] += a[i] + b.a[i],res.a[i+1] += res.a[i]/10,res.a[i] %= 10;
		if(res.a[res.a[0]+1])res.a[0] ++;
		return res;
	}
};

int main(){
	int n;
	scanf("%d",&n);
	Add ans,x;
	ans.read();
	for(int i=2;i<=n;i++){
		x.read();
		ans = ans + x;
	}
	ans.print();
	return 0;
}

T2

题目:

二叉树结点间的一种加权距离定义为:上行方向的变数×3 +下行方向的边数×2 。上行方向是指由结点向根的方向,下行方向是指与由根向叶结点方向。 给定一棵二叉树T及两个结点u和v,试求u到v的加权距离。

INPUT:

第1行,1个整数N,表示二叉树的结点数。

随后若干行,每行两个整数a和b,用空格分隔,表示结点a到结点b有一条边,a、b是结点的编号,1≤a、b≤N;根结点编号为1,边从根向叶结点方向。

最后1行,两个整数u和v,用空格分隔,表示所查询的两个结点的编号。

OUTPUT:

1行,1个整数,表示查询的加权距离。

Sample

INPUT

5
1 2
2 3
1 4
4 5
3 4

OUTPUT

8

Time Limit

100ms

Memory Limit

10MB

Data range

1≤N≤100000  1≤u、v≤N

Solution:

LCA,分三种情况,一是u是v祖先,一是v是u祖先,一是u和v的祖先是另一个点

赛时AC代码:

#include <bits/stdc++.h>

using namespace std;

const int N=100010;
int f[N][18],d[N],dis[N];
int ver[N],Next[N],head[N];
int s,n,m,tot,t;
queue<int> q;

int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return x;
}

void add(int u,int v){
	ver[++tot]=v;Next[tot]=head[u];head[u]=tot;
}

void bfs(int s){
	q.push(s);d[s]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=Next[i]){
			int y=ver[i];
			if(d[y])continue;
			d[y]=d[x]+1;
			f[y][0]=x;
			for(int j=1;j<=t;j++)f[y][j]=f[f[y][j-1]][j-1];
				q.push(y);
		}
	}
}

int lca(int x,int y){
	if(d[x]>d[y])swap(x,y);

	for(int i=t;i>=0;i--)
		if(d[f[y][i]]>=d[x])y=f[y][i];
	if(x==y)return x;

	for(int i=t;i>=0;i--)
		if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
}

int main(){
	n=read();
	s = 1;
	t=(int)(log(n)/log(2))+1;//t=logn
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		add(x,y);
	}
	bfs(s);
	int u = read(),v = read();
	int w = lca(u,v);
	if(w == u)printf("%d",2 * (d[v] - d[u]));
	else if(w == v)printf("%d",3 * (d[u] - d[v]));
	else printf("%d",3 * (d[u] - d[w]) + 2 * (d[v] - d[w]));
	return 0;
}

T3

题目:

长春市有n个交通枢纽,计划在1号枢纽到n号枢纽之间修建一条轻轨。轻轨由多段隧道组成,候选隧道有m段。每段候选隧道只能由一个公司施工,施工天数对各家公司一致。有n家施工公司,每家公司同时最多只能修建一条候选隧道。所有公司可以同时开始施工。请评估:修建这条轻轨最少要多少天。。

INPUT:

第1行,两个整数n和m,用空格分隔,分别表示交通枢纽的数量和候选隧道的数量。

第2行到第m+1行,每行三个整数a、b、c,用空格分隔,表示枢纽a和枢纽b之间可以修建一条双向隧道,施工时间为c天

OUTPUT:

输出一行,包含一个整数,表示最少施工天数。

Sample

INPUT

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

OUTPUT

6

Time Limit

500ms

Memory Limit

10MB

Data range

1 ≤ n ≤ 100000,1 ≤ m ≤ 200000 1 ≤ a, b ≤ n,1 ≤ c ≤ 1000000

Solution:

容易证明,在一个无负权的图中,任意最短路最多经过n-1条边,因为一条最短路不可能经过一个点两次,所以问题转化为了,寻找在1到n的所有最短路中经过的最大边最小的那条最短路。可以二分,也可以利用最小生成树,这里我用的是最小生成树

赛时AC代码:

#include <bits/stdc++.h>

using namespace std;

int n,m;
bool flag;
int vis[100003],fa[100005];
int to[200005],nex[200005],head[100005],val[200005],ce;
int stac[100005],top;
queue<int> q;

struct Node{
	int x,y,w;
}Ed[200005];

int read(){
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return x;
}

void add(int u,int v,int w){
	to[++ce] = v,nex[ce] = head[u],head[u] = ce,val[ce] = w;
}

void load(){
    for(int i=1;i<=n;i++)fa[i]=i;
}

int find(int x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}

void merge(int x,int y){
	fa[find(x)] = find(y);
}

bool cmp(Node a,Node b){
	return a.w < b.w;
}

void kruskal(){
	for(int i=1;i<=m;i++)
		if(find(Ed[i].y)!=find(Ed[i].x)){
			merge(Ed[i].x,Ed[i].y);
			add(Ed[i].x,Ed[i].y,Ed[i].w);
			add(Ed[i].y,Ed[i].x,Ed[i].w);
			if(find(1) == find(n))return;
		}
}

void dfs(int x){
	if(x == n){
		int ans = 0;
		for(int i=1;i<=top;i++)
			ans = max(ans,val[stac[i]]);
		printf("%d",ans);
		exit(0);
	}
	for(int i=head[x];i;i=nex[i]){
		int y = to[i];
		if(vis[y])continue;
		stac[++top] = i;
		vis[y] = 1;
		dfs(y);
		top--;
	}
}

int main(){
	n = read(),m = read();
	load();
	for(int i=1;i<=m;i++)
		Ed[i].x = read(),Ed[i].y = read(),Ed[i].w = read();
	sort(Ed+1,Ed+1+m,cmp);
	kruskal();
	vis[1] = 1;
	dfs(1);
	return 0;
}

T4

题目:

小唐正在学习数据结构。他尝试应用数据结构理论处理数据。最近,他接到一个任务,要求维护一个动态数据表,并支持如下操作:

  1. 插入操作(I):从表的一端插入一个整数。

  2. 删除操作(D):从表的另一端删除一个整数。

  3. 取反操作(R):把当前表中的所有整数都变成相反数。

  4. 取最大值操作(M):取当前表中的最大值。

    如何高效实现这个动态数据结构呢?

INPUT:

第1行,包含1个整数M,代表操作的个数。

第2到M+1行,每行包含1个操作。每个操作以一个字符开头,可以是I、D、R、M。如果是I操作,格式如下:I x, x代表插入的整数。 。

OUTPUT:

若干行,每行1个整数,对应M操作的返回值。如果M和D操作时队列为空,忽略对应操作。

Sample

INPUT

6
I 6
R
I 2
M
D
M

OUTPUT

2
2

Time Limit

500ms

Memory Limit

50MB

Data range

2≤M≤1000000  -10000000≤x≤10000000

Solution:

这里我的解法是线段树,每次插入删除后变化区间,在询问时将当前区间存下来,把询问离线化,之后对整个数列建树。

赛时AC代码:

#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;

int read(){
    int x=0,y=1;char ch=getchar();
    while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
    if(ch=='-')y=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return x*y;
}

int a[2000005];
int m,now,noww;

struct Req{
    int id,x,y;
}ask[2000005];

struct Tree{
    int mx,mn;
    bool lazy;
}v[10000005];

void pushup(int rt){
    v[rt].mx = max(v[rt<<1].mx,v[rt<<1|1].mx);
    v[rt].mn = min(v[rt<<1].mn,v[rt<<1|1].mn);
}

void pushdown(int rt){
    if(v[rt].lazy){
        v[rt<<1].lazy ^= 1;
        v[rt<<1|1].lazy ^= 1;
        v[rt<<1].mx = -v[rt<<1].mx;
        v[rt<<1].mn = -v[rt<<1].mn;
        if(v[rt<<1].mx < v[rt<<1].mn)swap(v[rt<<1].mx,v[rt<<1].mn);
        v[rt<<1|1].mx = -v[rt<<1|1].mx;
        v[rt<<1|1].mn = -v[rt<<1|1].mn;
        if(v[rt<<1|1].mx < v[rt<<1|1].mn)swap(v[rt<<1|1].mx,v[rt<<1|1].mn);
    }
    v[rt].lazy = 0;
}

void plant(int l,int r,int rt){
    if(l == r){
        v[rt].mx = v[rt].mn = a[l];
        return;
    }
    int mid = (l + r) / 2;
    plant(l,mid,rt<<1);
    plant(mid+1,r,rt<<1|1);
    pushup(rt);
}

void rotate(int L,int R,int l,int r,int rt){
    if(L <= l && r <= R){
        if(v[rt].lazy)v[rt].lazy = 0;
        else v[rt].lazy = 1;
        v[rt].mx = -v[rt].mx;
        v[rt].mn = -v[rt].mn;
        if(v[rt].mx < v[rt].mn)swap(v[rt].mx,v[rt].mn);
        return;
    }
    pushdown(rt);
    int mid = (l + r) / 2;
    if(L <= mid)rotate(L,R,l,mid,rt<<1);
    if(R > mid)rotate(L,R,mid+1,r,rt<<1|1);
    pushup(rt);
}

int MAX(int L,int R,int l,int r,int rt){
    if(L <= l && r <= R){
        return v[rt].mx;
    }
    pushdown(rt);
    int ans = -1e9,mid = (l + r) / 2;
    if(L <= mid)ans = max(ans,MAX(L,R,l,mid,rt<<1));
    if(R > mid)ans = max(ans,MAX(L,R,mid+1,r,rt<<1|1));
    pushup(rt);
    return ans;
}

int main(){
    m = read();
    int nowl = 1;
    for(int i=1;i<=m;i++){
        char s[10];
        scanf("%s",s);
        if(s[0] == 'I')a[++now] = read();
        else if(s[0] == 'D'){if(nowl > now)continue;nowl++;}
        else if(s[0] == 'R'){
            if(nowl > now)continue;
            ask[++noww].id = 1;
            ask[noww].x = nowl;
            ask[noww].y = now;
        }
        else if(s[0] == 'M'){
            if(nowl > now)continue;
            ask[++noww].id = 2;
            ask[noww].x = nowl;
            ask[noww].y = now;
        }
    }
    if(now)plant(1,now,1);
    for(int i=1;i<=noww;i++){
        int L = ask[i].x,R = ask[i].y;
        if(ask[i].id == 1){
            rotate(L,R,1,now,1);
        } else {
            printf("%d\n",MAX(L,R,1,now,1));
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值