7-1 高精度数加法
高精度数是指大大超出了标准数据类型能表示的范围的数,例如10000位整数。很多计算问题的结果都很大,因此,高精度数极其重要。
一般使用一个数组来存储高精度数的所有数位,数组中的每个元素存储该高精度数的1位数字或多位数字。 请尝试计算:N个高精度数的加和。这个任务对于在学习数据结构的你来说应该是小菜一碟。 。
输入格式:
第1行,1个整数N,表示高精度整数的个数,(1≤N≤10000)。
第2至N+1行,每行1个高精度整数x, x最多100位。
输出格式:
1行,1个高精度整数,表示输入的N个高精度数的加和。
输入样例:
在这里给出一组输入。例如:
3
12345678910
12345678910
12345678910
输出样例:
在这里给出相应的输出。例如:
37037036730
分析:
在高精度加法的基础上设置了加数的个数,增加了难度,每次高精度计算之后,只需要把和存放到保存第一个加数的字符数组中即可,运用循环控制加数的个数,在第一个加数输入之后,每次输入一个加数做一次处理,还要考虑到只有一个加数的情况,最后输出。
完整代码:
#include <bits/stdc++.h>
#include <vector>
using namespace std;
const int INF=1<<30;
const int MAX=1e5+5;
typedef long long ll;
const int MAXN = 10000; //根据题目的最大值。+4为了防止A+B出现进位
char s1[MAXN] = {};//存储字符串
char s2[MAXN] = {};//存储字符串
int a[MAXN] = {};//存储加数A
int b[MAXN] = {};//存储加数B
int c[MAXN] = {};//存储和B
int main() {
int n,len;
scanf("%d",&n);
if(n==1)//只有一个数字,不用算,直接输出
{
scanf("%s",s1);
printf("%s\n",s1);
return 0;
}
else
{
scanf("%s",s1);
//将字符串写入到数组A中
for(int kk=2;kk<=n;kk++)
{
scanf("%s",s2);//读入字符串
//将字符串写入到数组B中
int len1 = strlen(s1);
for (int i=0; i<len1; i++) {
//倒序写入
a[i] = s1[len1-i-1] - '0';
}
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;
}
}
for (int i = 0; i < len; i++) {
s1[len-1-i] = c[i] + '0';
}
for (int i = 0; i < len; i++) {
a[i] = 0;
b[i] = 0;
}
}
//逆序打印输出
for (int i=len-1; i>=0; i--) {
printf("%d", c[i]);
}
printf("\n");
}
return 0;
}
7-2 二叉树加权距离
二叉树结点间的一种加权距离定义为:上行方向的变数×3 +下行方向的边数×2 。上行方向是指由结点向根的方向,下行方向是指与由根向叶结点方向。 给定一棵二叉树T及两个结点u和v,试求u到v的加权距离。
输入格式:
第1行,1个整数N,表示二叉树的结点数,(1≤N≤100000)。
随后若干行,每行两个整数a和b,用空格分隔,表示结点a到结点b有一条边,a、b是结点的编号,1≤a、b≤N;根结点编号为1,边从根向叶结点方向。
最后1行,两个整数u和v,用空格分隔,表示所查询的两个结点的编号,1≤u、v≤N。
输出格式:
1行,1个整数,表示查询的加权距离。
输入样例:
在这里给出一组输入。例如:
5
1 2
2 3
1 4
4 5
3 4
输出样例:
在这里给出相应的输出。例如:
8
分析:
1、在建树的结构体中,我们只需要存放父亲结点,以及结点的深度,这里的深度指的是由跟出发,向下走的边数,默认跟的深度为1,在输入时,每次都要将儿子结点,在父亲结点深度的基础上加1。
2、计算距离是,考虑几种情况:
第一、两个结点是同一个结点。
第二、第一个结点的父亲是第二个结点。
第三、第二个结点的父亲是第一个结点。
第四、第一个结点的深度比第二个结点的深度大。
第五、第二个结点的深度比第一个结点的深度大。
第六、两个结点的在同一个深度。
完整代码:
#include<bits/stdc++.h>
#include<cstdio>
#include<stdlib.h>
#include<stack>
using namespace std;
const int MAX=100005;
struct node
{
int fa,deep;
}tree[MAX];
int n;
int main(){
int i,u,v;
scanf("%d",&n);
tree[1].deep=1;
for(i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
tree[v].fa=u;
tree[v].deep=tree[u].deep+1;
}
scanf("%d%d",&u,&v);
if(u==v)
{
printf("0");
return 0;
}
int deep1=0,deep2=0;
while(tree[u].fa!=tree[v].fa)
{
if(tree[u].fa==v)
{
cout<<(deep1+1)*3<<endl;
return 0;
}
if(tree[v].fa==u)
{
cout<<(deep2+1)*2<<endl;
return 0;
}
if(tree[u].deep>tree[v].deep)
{
deep1++;
u=tree[u].fa;
}
if(tree[v].deep>tree[u].deep)
{
deep2++;
v=tree[v].fa;
}
if(tree[u].deep==tree[v].deep&&tree[u].fa!=tree[v].fa)
{
deep1++;
u=tree[u].fa;
deep2++;
v=tree[v].fa;
}
}
cout<<(deep1+1)*3+(deep2+1)*2<<endl;
return 0;
}
7-3 修轻轨
长春市有n个交通枢纽,计划在1号枢纽到n号枢纽之间修建一条轻轨。轻轨由多段隧道组成,候选隧道有m段。每段候选隧道只能由一个公司施工,施工天数对各家公司一致。有n家施工公司,每家公司同时最多只能修建一条候选隧道。所有公司可以同时开始施工。请评估:修建这条轻轨最少要多少天。。
输入格式:
第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。
输出格式:
输出一行,包含一个整数,表示最少施工天数。
输入样例:
在这里给出一组输入。例如:
6 6
1 2 4
2 3 4
3 6 7
1 4 2
4 5 5
5 6 6
输出样例:
在这里给出相应的输出。例如:
6
分析:
最小支撑树问题,用Kruskal算法,,只需要把1和n结点连通即可。因此循环退出条件就是find(1)==find(n),两个结点连通即可退出。施工公司可以同时施工,那么最长时间必然是最小支撑树上权值最大边,最后输出即可。
完整代码:
#include <bits/stdc++.h>
#include <vector>
using namespace std;
const int INF=1<<30;
const int MAX=200005;
struct edge
{
int fr,to;
long long w;
bool operator<(const edge& b)const
{
return w<b.w;
}
}a[MAX];
int fa[MAX];
int find(int x)
{
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
int a=find(x),b=find(y);
if(a!=b)
fa[a]=b;
}
int main()
{
int n,m;
long long ans=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
scanf("%d %d %lld",&a[i].fr,&a[i].to,&a[i].w);
}
sort(a+1,a+m+1);
int cnt=0;
for(int i=1;i<=m&&cnt!=n-1;i++)
{
if(find(a[i].fr)!=find(a[i].to))
{
cnt++;
ans=a[i].w;
merge(a[i].fr,a[i].to);
}
if(find(1)==find(n))break;
}
printf("%lld\n",ans);
return 0;
}
7-4 数据结构设计I
小唐正在学习数据结构。他尝试应用数据结构理论处理数据。最近,他接到一个任务,要求维护一个动态数据表,并支持如下操作:
-
插入操作(I):从表的一端插入一个整数。
-
删除操作(D):从表的另一端删除一个整数。
-
取反操作(R):把当前表中的所有整数都变成相反数。
-
取最大值操作(M):取当前表中的最大值。
如何高效实现这个动态数据结构呢?
输入格式:
第1行,包含1个整数M,代表操作的个数, 2≤M≤1000000。
第2到M+1行,每行包含1个操作。每个操作以一个字符开头,可以是I、D、R、M。如果是I操作,格式如下:I x, x代表插入的整数,-10000000≤x≤10000000。 。
输出格式:
若干行,每行1个整数,对应M操作的返回值。如果M和D操作时队列为空,忽略对应操作。
输入样例:
在这里给出一组输入。例如:
6
I 6
R
I 2
M
D
M
输出样例:
在这里给出相应的输出。例如:
2
2
分析:
一端插入,一端删除,考虑用双端队列。取反操作可以设置一个变量,每次取反它都进行非运算,最后根据这个值判断数字输出时是否应该取反。维护一个区间,用top表示头指针,fr表示尾指针。
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int MAX= 1e6+5;
deque<int>maxx,minn;
int flag;
int a[MAX];
int top,fr;
int main()
{
int m;
scanf("%d",&m);
char x[2];
while(m--)
{
scanf("%s",x);
if(x[0]=='I')
{
top++;
scanf("%d",&a[top]);
if(flag)
a[top]=-a[top];
int tmp=a[top];
while(!maxx.empty()&&a[maxx.back()]>=tmp)maxx.pop_back();
maxx.push_back(top);
while(!minn.empty()&&a[minn.back()]<=tmp)minn.pop_back();
minn.push_back(top);
}
else if(x[0]=='R')
{
flag^=1;
}
else if(x[0]=='D')
{
if(fr<top)
{
fr++;
while(!maxx.empty()&&maxx.front()<=fr)maxx.pop_front();
while(!minn.empty()&&minn.front()<=fr)minn.pop_front();
}
}
else if(x[0]=='M')
{
if(fr<top)
{
if(flag)
{
printf("%d\n",-a[maxx.front()]);
}
else
{
printf("%d\n",a[minn.front()]);
}
}
}
}
return 0;
}