7-1 高精度数加法 (100 分)
高精度数是指大大超出了标准数据类型能表示的范围的数,例如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>
using namespace std;
vector<int> add(vector<int>& A, vector<int>& B)
{
vector<int> C;
if(B.size() > A.size()) return add(B, A);
int t = 0;
for(int i = 0; i < A.size(); i++)
{
t += A[i];
if(i < B.size()) t += B[i];
C.push_back(t % 10);
t = t / 10;
}
if(t) C.push_back(t);
return C;
}
vector<int> A,B;
int main()
{
int N;
scanf("%d\n",&N);
char num[110];
for(int i=1;i<=N;i++)
{
scanf("%s",num);
int len=strlen(num);
A.clear();
for(int j=len-1;j>=0;j--)
{
A.push_back(num[j]-'0');
}
B=add(A,B);
//printf("%d %d \n",A.size(),B.size());
}
for(int i=B.size()-1;i>=0;i--)
{
printf("%d",B[i]);
}
return 0;
}
7-2 二叉树加权距离 (100 分)
二叉树结点间的一种加权距离定义为:上行方向的变数×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
二叉树也是图的一种,直接用图的最短路算法感觉很简单。这里我用了spfa算法
#include<vector>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<climits>
#include<queue>
using namespace std;
struct node{
int to;
int cost;
};
const int maxn=100010;
vector<node> gph[maxn];
int path[maxn],dist[maxn];
bool mark[maxn];
queue<int> q;
void spfa(int v,int n)
{
for(int i=1;i<=n;i++) dist[i]=0x3f3f3f3f;
dist[v]=0;
q.push(v);
mark[v]=1;
while(!q.empty())
{
int u=q.front();
mark[u]=0;
q.pop();
vector<node>::iterator it;
for(it=gph[u].begin();it!=gph[u].end();it++)
{
int k=it->to;
if(dist[u]+it->cost<dist[k])
{
dist[k]=dist[u]+it->cost;
if(!mark[k])
{
q.push(k);
mark[k]=1;
}
}
}
}
}
int main()
{
int n;
scanf("%d",&n);
node tmp;
int u,v;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
tmp.to=v;
tmp.cost=2;
gph[u].push_back(tmp);
tmp.to=u;
tmp.cost=3;
gph[v].push_back(tmp);
}
scanf("%d%d",&u,&v);
spfa(u,n);
printf("%d",dist[v]);
return 0;
}
其实我当时想的是,把u作为根节点,用dfs算出最短路径,但是内存会超限。
这个方法拿了70分,也许把dfs消除递归就好了,代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
struct node{
int to;
int cost;
};
vector<node> gph[maxn];
bool vis[maxn];
int sum=0;
void dfs(int u,int v)
{
vis[u]=1;
if(u==v)
{
printf("%d",sum);
exit(0);
}
for(int i=0;i<gph[u].size();i++)
{
if(!vis[gph[u][i].to])
{
sum+=gph[u][i].cost;
dfs(gph[u][i].to,v);
sum-=gph[u][i].cost;
}
}
}
int main()
{
int n;
scanf("%d",&n);
node tmp;
int u,v;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&u,&v);
tmp.to=v;
tmp.cost=2;
gph[u].push_back(tmp);
tmp.to=u;
tmp.cost=3;
gph[v].push_back(tmp);
}
scanf("%d%d",&u,&v);
dfs(u,v);
return 0;
}
7-3 修轻轨 (100 分)
长春市有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
这题,我当时题目没有看明白,也完全没有思路。
“计划在1号枢纽到n号枢纽之间修建一条轻轨”,意思是只要求1号和n号是连通的,其他的线只是顺便修建一下。然后,连接图中的两个点,最多只需要n-1条边,而题目中说有n家公司同时修建。那其实,我们只要找出那最多n-1条边是哪些边就可以了。就可以用一个对kruskal算法稍作改动的方法来解决,即把找到n-1条边这个结束循环的条件,改为1和n已经连通即可,此时,最后被选择的那条边,对应的就是最短天数。
#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
class UFSET{//使用路径压缩和按秩合并
public:
int father[maxn];
int find(int x);
void unite(int x,int y);
};
int UFSET::find(int x)
{
if(father[x]<=0)return x;
father[x]=find(father[x]);
return father[x];
}
void UFSET::unite(int x,int y)//把y接到x上去
{
int fx=find(x);
int fy=find(y);
if(fx==fy)
return;
if(father[fx]<father[fy])
father[fy]=x;
else
{
if(father[fx]==father[fy])father[fx]--;
father[fy]=fx;
}
}
UFSET h;
struct edge{
int u,v,w;
}e[maxn],te[maxn];
bool cmp(const edge& a,const edge& b)
{
return a.w<b.w;
}
void kruskal(int n,int m)//点数,边数
{
sort(e+1,e+m+1,cmp);
for(int i=1,j=0;i<=m;i++)
{
int u=e[i].u;
int v=e[i].v;
if(h.find(u)!=h.find(v))
{
te[++j]=e[i];
h.unite(u,v);
}
if(h.find(1)==h.find(n))
{
printf("%d",te[j].w);
break;
}
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1,a,b,c;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
e[i].u=a;
e[i].v=b;
e[i].w=c;
}
if(n==1)
{
printf("0");
return 0;
}
kruskal(n,m);
return 0;
}
我这个方法,对n=1这种情况要单独处理。
7-4 数据结构设计I (100 分)
小唐正在学习数据结构。他尝试应用数据结构理论处理数据。最近,他接到一个任务,要求维护一个动态数据表,并支持如下操作:
-
插入操作(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
一端插入,一端删除,可以联想到队列。要求一段区间上的最值,可以用单调队列。至于取相反数,可以引入一个标记flag,每输入一次R就改变一次状态,根据flag来确定对于新插入的数应该采用原值还是相反数,以及输出最大值时应当如何操作(我当时并没有想到这个,我当时只是意识到不能直接取所有数的相反数然后重新计算最大值,于是应当引入一些标记,但没有想到这种标记应该是对后续的操作有影响而不是修改以前的数) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
int flag=0;
deque<int> big,sml;
int a[maxn];
int ff,rr;
int main()
{
int M;
scanf("%d",&M);
char op='\0';
for(int i=1;i<=M;i++)
{
while(op!='I'&&op!='R'&&op!='M'&&op!='D') op=getchar();
if(op=='I')
{
scanf("%d",a+rr);
if(flag) a[rr]=-1*a[rr];
while(!big.empty()&&a[big.back()]<=a[rr]) big.pop_back();
big.push_back(rr);
while(!sml.empty()&&a[sml.back()]>=a[rr]) sml.pop_back();
sml.push_back(rr);
rr++;
}
else if(op=='D')
{
if(ff<rr)
{
if(ff==big.front())big.pop_front();
if(ff==sml.front())sml.pop_front();
ff++;
}
}
else if(op=='R')
{
flag=flag^1;
}
else if(op=='M')
{
if(ff<rr)
{
if(flag)
{
printf("%d\n",-1*a[sml.front()]);
}
else
{
printf("%d\n",a[big.front()]);
}
}
}
op='\0';
}
return 0;
}