**数据结构第二次实验解题报告
7-1 数列查询 (100 分)**
已知数列的通项公式为:
f(n) = f(n-1)*11/10,f[1]=10.
通项从左向右计算,*和/分别表示整数乘法和除法。 现在,要多次查询数列项的值。
输入格式:
第1行,1个整数q,表示查询的次数, 1≤q≤10000. 第2至q+1行,每行1个整数i,表示要查询f(i)的值。
输出格式:
q行,每行1个整数,表示f(i)的值。查询的值都在32位整数范围内。
输入样例:
在这里给出一组输入。例如:
3
1
2
3
输出样例:
在这里给出相应的输出。例如:
10
11
12
解答:
**解法一:**如果按照题意我们很容易的就想到使用递归的方法,但递归是非常费时间的,这里的时间限制是10ms,显然直接递归会超时。
我们可以用一个数组储存递归的得到的值避免重复递归,若值不为0则递归,若为0则进行递归再把得到的值储存在数组中。
具体实现如下:
#include<stdio.h>
int a[10000];
int f(int n){
if(n==1) return a[1];
else {
if(a[n]!=0)return a[n];
else {a[n]=f(n-1)*11/10;return a[n];}
}
}
int main(){
int q,k;
scanf("%d",&q);
a[1]=10;
for(int i=1;i<=q;i++){
scanf("%d",&k);
printf("%d\n",f(k));
}
return 0;
}
**解法二:**也可以把所有值实现全部算出来,直接进行打表,也可减少时间复杂度。
具体实现如下:
#include<stdio.h>
int main()
{
int a[10000];
int i;
a[0]=10;
for(i=1;i<10000;i++)
a[i]=a[i-1]*11/10;
int q;
scanf("%d",&q);
int b[q];
for(i=0;i<q;i++)
scanf("%d",&b[i]);
for(i=0;i<q-1;i++)
printf("%d\n",a[b[i]-1]);
printf("%d",a[b[q-1]-1]);
}
7-2 稀疏矩阵之和 (100 分)
矩阵A和B都是稀疏矩阵。请计算矩阵的和A+B.如果A、B不能做和,输出“Illegal!”
输入格式:
矩阵的输入采用三元组表示,先A后B。对每个矩阵:
第1行,3个整数N、M、t,用空格分隔,分别表示矩阵的行数、列数和非0数据项数,10≤N、M≤50000,t≤min(N,M).
第2至t+1行,每行3个整数r、c、v,用空格分隔,表示矩阵r行c列的位置是非0数据项v, v在32位有符号整型范围内。三元组默认按行列排序。
输出格式:
矩阵A+B,采用三元组表示,默认按行列排序,非零项也在32位有符号整型范围内。
输入样例:
10 10 3
2 2 2
5 5 5
10 10 20
10 10 2
2 2 1
6 6 6
输出样例:
10 10 4
2 2 3
5 5 5
6 6 6
10 10 20
解答:这是老师让我们复习的三元组表处理稀疏矩阵,我们可以用一个新的三元组表储存加完后的三元组表,把合成的过程分为三种情况,第一是两个元素的行和列均相等,就把两个元素的数值相加放到第三个组中,第二种是行相等但第二个元素的列比第一个元素大,第三种是第二个元素的行大于第一个元素。特别要注意的如果两个元素相加得0要把这个元素剔除。
关键部分如下:
if(a[l1].r==b[i].r&&a[l1].c==b[i].c)
{
c[l2].r=a[l1].r;
c[l2].c=a[l1].c;
c[l2].v=a[l1].v+b[i].v;
l1++,l2++;
if(c[l2-1].v==0)
{
l2--;
}
continue;
}
else
while(l1!=t1)
{
if(a[l1].r==b[i].r&&a[l1].c<b[i].c)
{
c[l2].r=a[l1].r;
c[l2].c=a[l1].c;
c[l2].v=a[l1].v;
l1++,l2++;
}
else if(a[l1].r<b[i].r)
{
c[l2].r=a[l1].r;
c[l2].c=a[l1].c;
c[l2].v=a[l1].v;
l1++,l2++;
}
具体实现如下:
#include<stdio.h>
#include<stdlib.h>
struct node{
int r,c,v;
};
struct node a[50010],b[50010],c[110000];
int main(){
int m1,n1,t1,m2,n2,t2;
int i=0,l1=0,l2=0,flag;
scanf("%d %d %d",&m1,&n1,&t1);
for(i=0;i<t1;i++)
{
scanf("%d %d %d",&a[i].r,&a[i].c,&a[i].v);
}
scanf("%d %d %d",&m2,&n2,&t2);
if(m1!=m2||n1!=n2)
{ printf("Illegal!");
return 0;
}
flag=1;
for(i=0;i<t2;i++)
{
scanf("%d %d %d",&b[i].r,&b[i].c,&b[i].v);
if(l1<t1)
{
if(a[l1].r==b[i].r&&a[l1].c==b[i].c)
{
c[l2].r=a[l1].r;
c[l2].c=a[l1].c;
c[l2].v=a[l1].v+b[i].v;
l1++,l2++;
if(c[l2-1].v==0)
{
l2--;
}
continue;
}
else
while(l1!=t1)
{
if(a[l1].r==b[i].r&&a[l1].c<b[i].c)
{
c[l2].r=a[l1].r;
c[l2].c=a[l1].c;
c[l2].v=a[l1].v;
l1++,l2++;
}
else if(a[l1].r<b[i].r)
{
c[l2].r=a[l1].r;
c[l2].c=a[l1].c;
c[l2].v=a[l1].v;
l1++,l2++;
}
else break;
}
}
c[l2].r=b[i].r;
c[l2].c=b[i].c;
c[l2].v=b[i].v;
l2++;
}
while(l1<t1)
{
c[l2].r=a[l1].r;
c[l2].c=a[l1].c;
c[l2].v=a[l1].v;
l1++,l2++;
}
printf("%d %d %d\n",m1,n1,l2);
for(i=0;i<l2;i++)
{
printf("%d %d %d\n",c[i].r,c[i].c,c[i].v);
}
}
7-3 文字编辑 (100 分)
一篇文章由n个汉字构成,汉字从前到后依次编号为1,2,……,n。 有四种操作:
A i j表示把编号为i的汉字移动编号为j的汉字之前;
B i j表示把编号为i的汉字移动编号为j的汉字之后;
Q 0 i为询问编号为i的汉字之前的汉字的编号;
Q 1 i为询问编号为i的汉字之后的汉字的编号。
规定:1号汉字之前是n号汉字,n号汉字之后是1号汉字。
输入格式:
第1行,1个整数T,表示有T组测试数据, 1≤T≤9999.
随后的每一组测试数据中,第1行两个整数n和m,用空格分隔,分别代表汉字数和操作数,2≤n≤9999,1≤m≤9999;第2至m+1行,每行包含3个常量s、i和j,用空格分隔,s代表操作的类型,若s为A或B,则i和j表示汉字的编号,若s为Q,i代表0或1,j代表汉字的编号。
输出格式:
若干行,每行1个整数,对应每个询问的结果汉字编号。
输入样例:
在这里给出一组输入。例如:
1
9999 4
B 1 2
A 3 9999
Q 1 1
Q 0 3
输出样例:
在这里给出相应的输出。例如:
4
9998
解答:如果数组来储存,查询的时间复杂度是O(1)的,但一个插入的时间复杂度就是O(n)的,如果用链表插入的时间复杂度变成了O(1),但查询的时间复杂度就变成了O(n),由题目可得这是围绕数的前驱节点和后继结点来操作,我们可以用静态链表来储存数据和数据的前驱节点和后继节点的下标,这样可以大大的减小时间复杂度。
具体实现如下:
#include<stdio.h>
struct node
{
int f,r;
}a[100000];
void front(int i,int j);
void back(int i,int j);
int main()
{
int t,n,m;
int i,j,x,y;
char k;
scanf("%d",&t);
for(i=0;i<t;i++)
{
scanf("%d %d",&n,&m);
a[n].r=1;a[1].f=n;
for(j=1;j<n;j++)
{
a[j].r=j+1;
a[j+1].f=j;
}
for(j=0;j<m;j++)
{
scanf("%c%d%d",&k,&x,&y);
scanf("%c%d%d",&k,&x,&y);
if(k=='Q')
{
if(x==1)printf("%d\n",a[y].r);
else if(x==0)printf("%d\n",a[y].f);
}
else if(k=='A')
{
front(x,y);
}
else
{
back(x,y);
}
}
}
return 0;
}
void front(int i,int j){
int r,t,k,n;
if(a[i].r==j)return;
if(a[j].r!=i){
r=a[i].f;
k=a[i].r;
n=a[j].f;
a[r].r=k;
a[k].f=r;
a[n].r=i;
a[i].f=n;
a[j].f=i;
a[i].r=j;
}
else {
k=a[i].r;
n=a[j].f;
a[k].f=j;
a[j].r=k;
a[j].f=i;
a[i].r=j;
a[i].f=n;
a[n].r=i;
}
return;
}
void back(int i,int j)
{
int r,t,k,n;
if(a[j].r==i)return;
if(a[i].r!=j){
r=a[i].f;
k=a[i].r;
t=a[j].r;
a[r].r=k;
a[k].f=r;
a[i].f=j;
a[j].r=i;
a[i].r=t;
a[t].f=i;
}
else {
r=a[i].f;
t=a[j].r;
a[r].r=j;
a[j].f=r;
a[i].f=j;
a[j].r=i;
a[i].r=t;
a[t].f=i;
}
return;
}
7-4 幸福指数 (100 分)
人生中哪段时间最幸福?幸福指数可能会帮你发现。幸福指数要求:对自己每天的生活赋予一个幸福值,幸福值越大表示越幸福。一段时间的幸福指数就是:这段时间的幸福值的和乘以这段时间的幸福值的最小值。幸福指数最大的那段时间,可能就是人生中最幸福的时光。
输入格式:
第1行,1个整数n,, 1≤n≤100000,表示要考察的天数。
第2行,n个整数Hi,用空格分隔,Hi表示第i天的幸福值,0≤n≤1000000。
输出格式:
第1行,1个整数,表示最大幸福指数。
第2行,2个整数l和r,用空格分隔,表示最大幸福指数对应的区间[l,r]。如果有多个这样的区间,输出最长最左区间。
输入样例:
在这里给出一组输入。例如:
7
6 4 5 1 4 5 6
输出样例:
在这里给出相应的输出。例如:
60
1 3
解答:如果枚举区间的话,会造成很多区间的包含,导致做很多重复且无用的操作,我们把枚举区间优化成枚举以这个值为最小值的区间,n个值只需枚举n个区间就行了,开始我用的暴力法,直接从这个值开始向左找第一个比他大的值,再向右找第一个比他大的值,从而确定区间再计算区间的值,并输出最大值,但中间比较的操作过多导致最后一个运行超时了。
#include<stdio.h>
struct data
{
int left;
int right;
};
struct data b[100000];
int a[100000];
long long c[100000];
int main()
{
int n,i;
long long max;
int fro,rear;
scanf("%d",&n);
scanf("%d",&a[0]);
c[0]=a[0];
for(i=1;i<n;i++)
{
scanf("%d",&a[i]);
c[i]=c[i-1]+a[i];
}
max=-1;
fro=-1;
rear=-1;
for(i=0;i<n;i++)
{
b[i].left=b[i].right=i;
if(b[i].left!=0)
{
while(b[i].left!=1&&a[b[i].left-1]>=a[i])
{
b[i].left--;
}
if(b[i].left==1&&a[0]>=a[i])
b[i].left--;
}
if(b[i].right!=n-1)
{
while(b[i].right!=n-2&&a[b[i].right+1]>=a[i])
{
b[i].right++;
}
if(b[i].right==n-2&&a[n-1]>=a[i])
b[i].right++;
}
long long tem;
if(b[i].left==0)
{
tem=c[b[i].right]*a[i];
}
else
{
tem=(c[b[i].right]-c[b[i].left-1])*a[i];
}
if(tem>max)
{
max=tem;
fro=b[i].left;
rear=b[i].right;
}
else if(tem==max)
{
if(rear-fro<b[i].right-b[i].left)
{
fro=b[i].left;
rear=b[i].right;
}
}
}
printf("%lld\n",max);
printf("%d %d\n",fro+1,rear+1);
}
可以用单调栈来优化寻找区间的过程。
#include<bits/stdc++.h>
using namespace std;
int left1[100000];
int right1[100000];
int a[100000];
long long int c[100000];
stack<int> q;
int main()
{
int n,i;
long long max=0,temp=0;
int fro,rear;
scanf("%d",&n);a[0]=a[n+1]=-10000000;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
c[i]=c[i-1]+a[i];
}
q.push(0);
for(i=1;i<=n;i++)
{
while(!q.empty()&&a[q.top()]>=a[i])
{
q.pop();
}
left1[i]=q.top();
q.push(i);
}
while(!q.empty())
{
q.pop();
}
q.push(n+1);
for(i=n;i>=1;i--)
{
while(!q.empty()&&a[q.top()]>=a[i])
{
q.pop();
}
right1[i]=q.top();
q.push(i);
}
for(i=1;i<=n;i++)
{
temp=(c[right1[i]-1]-c[left1[i]])*a[i];
if(temp>max)
{
max=temp;
fro=left1[i]+1;
rear=right1[i]-1;
}
else if(temp==max)
{
if(right1[i]-1-left1[i]-1>rear-fro)
{
fro=left1[i]+1;
rear=right1[i]-1;
}
else if(left1[i]+1<fro)
{
max=temp;
fro=left1[i]+1;
rear=right1[i]-1;
}
}
}
printf("%lld\n",max);
printf("%d %d\n",fro,rear);
}