title

【JLU数据结构荣誉课】第六次上机实验

->点我进原题
-> 7-1 高精度数加法
-> 7-2 二叉树加权距离
-> 7-3 修轻轨
-> 7-4 数据结构设计I

7-1 高精度数加法 (100 分)


代码长度限制 \(16 KB\)
时间限制 \(100 ms\)
内存限制 \(1 MB\)


Description

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

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

Input

\(1\) 行,\(1\) 个整数 \(N\),表示高精度整数的个数,\((1≤N≤10000)\)

\(2\)\(N+1\) 行,每行1个高精度整数 \(x\), \(x\) 最多100位。

Output

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

Sample Input

3
12345678910
12345678910
12345678910

Sample Output

37037036730

思路

高精度加法,这里不再赘述,但是考试时用的string,很玄学的有一个点一直RE,所以改成了char数组。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
inline int read(){
	int x=0,f=0;
	char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
int qa[1000],qb[1000],ans[1000],n,len1,len2;
int tot;
char a[1000], b[1000];
inline bool jud(int a){
	if(a>9)
		return 1;
	return 0;
}
signed main(){
	tot = read();
	cin>>a;
	if(tot==1){
		cout<<a;
		return 0;
	}
	for(int i=2;i<=tot;++i){
		cin>>b;
//		cout << a << endl << b << endl;
		len1=strlen(a);
		len2=strlen(b);
		for(int i=0;i<1000;++i)	qa[i]=qb[i]=ans[i]=0;
		n=max(len1,len2);
		for(int i=1;i<=n;++i){
			if(len1<i)	qa[i]=0;
			else	qa[i]=a[len1-i]-'0';
			if(len2<i)	qb[i]=0;
			else	qb[i]=b[len2-i]-'0';
		}
//		for(int i=n;i;--i)	cout<<qa[i];
//		for(int i=n;i;--i)	cout<<qb[i];
		for(int i=1;i<=n;++i){
			ans[i]+=qa[i]+qb[i];
			if(jud(ans[i])){
				ans[i]%=10;
				ans[i+1]++;
				if(i==n)	n++;
			}
		}
		for(int i=0;i<n;++i)	a[i]=ans[n-i]+'0';
		a[n+1]='\0';
	}
	for(int i=n;i;--i)	printf("%d", ans[i]);
	return 0;
}





7-2 二叉树加权距离 (100 分)


代码长度限制 \(16 KB\)
时间限制 \(100 ms\)
内存限制 \(10 MB\)


Description

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

Input

第1行,1个整数 \(N\),表示二叉树的结点数,\((1≤N≤100000)\)

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

最后1行,两个整数 \(u\)\(v\),用空格分隔,表示所查询的两个结点的编号,\(1≤u、v≤N\)

Output

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

Sample Input

5
1 2
2 3
1 4
4 5
3 4

Sample Output

8

思路

(1)LCA,在上跳下跳时记录走过路径个数,并乘以对应边权,较复杂,这里不推荐;
(2)最短路,将二叉树抽象成一个正向边权为2,反向边权为3的双向图,求点 \(u\)\(v\) 的最短路即可,下面给出该做法的代码。

代码

#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 500010
#define inf 2147483647
using namespace std;
int n,st,ed,dis[N],head[N],tot;
bool vis[N];
queue<int> q;
struct edge{
	int nxt,to,w;
}e[N];
inline int read(){
	int x=0,f=0;
	char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
inline void init(int s){
	for(int i=1;i<=n;i++){
		dis[i]=inf;
		vis[i]=false;
	}
	dis[s]=0;
	q.push(s);
}
inline void add(int u,int v,int w){
	e[++tot].to=v;
	e[tot].nxt=head[u];
	e[tot].w=w;
	head[u]=tot;
}
inline void spfa(int s){
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=false;
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].w){
				dis[v]=dis[u]+e[i].w;
				if(!vis[v]){
					vis[v]=true;
					q.push(v);
				}
			}
		}
	}
}
int main(){
	n=read();
	for(int i=1,u,v,w;i<n;i++){
		u=read();v=read();
		add(u,v,2);
		add(v,u,3);
	}
	st=read(),ed=read();
	init(st);
	spfa(st);
	printf("%d",dis[ed]);
	return 0;
}




7-3 修轻轨 (100 分)


代码长度限制 \(16 KB\)
时间限制 \(500 ms\)
内存限制 \(10 MB\)


Description

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

Input

\(1\) 行,两个整数 \(n\)\(m\),用空格分隔,分别表示交通枢纽的数量和候选隧道的数量,\(1 ≤ n ≤ 100000\)\(1 ≤ m ≤ 200000\)

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

Output

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

Sample Input

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

Sample Output

6

思路

题目中说求从点 \(1\) 到点 \(n\) 的最短距离,为使所用到的边的边权尽可能小,我们可以使用Kluskal,但程序的终止条件不是建成一棵树,而是点 \(1\) 和点 \(n\) 联通时终止,该边边权即为通道中最大边权。

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#define N 400010
using namespace std;
struct edge{
	int u,v,w;
} e[N];
int n,m,ans,f[N],cnt;
inline int read(){
	int f=0,x=0;
	char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
inline bool cmp(edge a,edge b){
	return a.w<b.w;
}
inline int find(int v){
	return f[v]==v?v:f[v]=find(f[v]);
}
inline bool merge(int x,int y){
	int t1=find(x);
	int t2=find(y);
	if(t1!=t2){
		f[t2]=t1;
		return true;
	}
	return false;
}
int main(){
	n=read();
	m=read();
	for(int i=1; i<=n; i++) f[i]=i;
	for(int i=1; i<=m; i++){
		e[i].u=read();
		e[i].v=read();
		e[i].w=read();
	}
	sort(e+1,e+m+1,cmp);
	for(int i=1; i<=m; i++){
		if(merge(e[i].u,e[i].v)){
			ans=max(ans,e[i].w);
		}
		if(find(1) == find(n))	break;
	}
	printf("%d",ans);
	return 0;
}




7-4 数据结构设计I (100 分)


代码长度限制 \(16 KB\)
时间限制 \(500 ms\)
内存限制 \(50 MB\)


Description

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

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

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

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

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

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

Input

\(1\) 行,包含 \(1\) 个整数 \(M\),代表操作的个数, \(2≤M≤1000000\)

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

Output

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

Sample Input

6
I 6
R
I 2
M
D
M

Sample Output

2
2

思路

观察数据范围,考虑 \(O(m)\) 做法,但这题数据出的比较水,像一些线段树、multiset这种 \(O(mlogm)\) 的做法也就过了,但是当时只想着 \(O(m)\) 做法了,于是便写出了全场唯一一个单调队列。
具体做法如下:建两个单调队列 \(q1,q2\),分别记录反转前的最大值和反转后的最大值,并记录一个反转标记,当标记了反转时将读入的数放入 \(q2\),将其相反数放入 \(q1\),反之同理,其余三个操作不再赘述。

代码

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define rg register
#define ll long long
using namespace std;
inline int read(){
	rg int f = 0, x = 0;
	rg char ch = getchar();
	while(!isdigit(ch))	f |= (ch == '-'), ch = getchar();
	while( isdigit(ch))	x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
	return f? -x: x;
}
int n, m;
int a1[1000010], q1[1000010], a2[1000010], q2[1000010];
int f1 = 1, r1 = 1, f2 = 1, r2 = 1;
int rev = 0, cnt = 0;
const int inf = 0x7fffffff;
signed main(){
	m = read();
	q1[0] = 0, a1[0] = -inf, a2[0] = +inf, q2[0] = 0;
	for(rg int i = 1; i <= m; ++i){
		char ch[1];
		scanf("%s", ch);
		switch(ch[0]){
			case 'D' :{
				if(cnt < n){
					cnt ++;
					while(f1 < r1 && q1[f1] <= cnt)	f1 ++;
					while(f2 < r2 && q2[f2] <= cnt)	f2 ++;
				}
				getchar();
				break;
			}
			case 'I' :{
				if(rev){
					a2[++n] = read();
					a1[n] = -a2[n];	
				} else{
					a1[++n] = read();
					a2[n] = -a1[n];	
				}	
				while(f1 < r1 && a1[q1[r1 - 1]] < a1[n])	r1 --;
				q1[r1 ++] = n;
				while(f2 < r2 && a2[q2[r2 - 1]] < a2[n])	r2 --;
				q2[r2 ++] = n;
				break;
			}
			case 'R' :{
				rev = (rev + 1) % 2;
				getchar();
				break;
			}
			case 'M' :{
				if(cnt < n){
					if(rev)	printf("%d\n", a2[q2[f2]]);
					else	printf("%d\n", a1[q1[f1]]);	
				}	
				getchar();
				break;
			}
		}
		
	}
	return 0;
}
posted @ 2021-06-12 01:16  Horrigue_JyowYang  阅读(81)  评论(0编辑  收藏  举报