数据结构-第二次上机实验-解题报告

一、7-1数列查询

题目

已知数列的通项公式为:

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

题目来源

思路

方法一:【递归】看到f(n)与f(n-1)的关系,很容易想到,用递归实现。不过,递归效率较低。只过了50%。
方法二:【枚举打表】既然递归能够实现,所以在递归失败后,我想到了用记录每一个n对应的f(n)的值的方式,来实现值的获取。(当然,要选效率高,我们就得确定最大的n值,进行枚举,否则枚举10000个,没有必要。故,用max[i]存储每个输入值,用来更新Max,以获得最大值)

代码

#include<iostream>
#include<string>
using namespace std;
int a[10001];
int main(){
	int q;cin>>q;
	int n;
	a[1]=10;
	int max[10001];
	cin>>max[1];
	int Max=max[1];
	for(int i=2;i<=q;i++)
	{
		cin>>max[i];
		if(Max<max[i]) Max=max[i];
	}
	for(int i=2;i<=Max;i++)
	{
		a[i]=a[i-1]*11/10;
	}
	for(int i=1;i<=q;i++)
	{
		printf("%d\n",a[max[i]]); 
	}
	return 0;
}

感悟

当然,要想效率高,我们就得确定最大的n值进行枚举,否则枚举10000个,没有必要。故,用max[i]存储每个输入值,用来更新Max,以获得最大值。
还有一点,cout的效率比printf低下,这个问题将在for循环次数过大时放大。所以追求效率时,应当选取scanf与printf。

二、7-2稀疏矩阵之和

题干

矩阵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

题目限制

思路

稀疏矩阵相加,通过老师的暗示 ,通过学习,我们知道,可以用三元组来存储稀疏矩阵来提高空间利用率,而不直接用二维数组的方式。

三元组代码

typedef struct{
	int i,j;
	int value;
}Three;//三元组结点
typedef struct
{
	Three a[50001];
	int imax,jmax;
	int size;
}Tmatrix;//三元组表示的稀疏矩阵

通过题目的分析,显然应该分多类多层讨论。
矩阵A,B行列不相等不可相加减,故输出illegal,这个应该都能考虑到
可相加减时,将在代码中做详细分类讨论。

代码

#include<bits/stdc++.h>
using namespace std;
typedef struct{
	int i,j;
	int value;
}Three;
typedef struct
{
	Three a[50001];
	int imax,jmax;
	int size;
}Tmatrix;


void initial(Tmatrix *T,int x,int y)
{
	T->imax=x;
	T->jmax=y;
	T->size=0;
}

void insert_T(Tmatrix *T,int i,int j,int n)
{
	T->size++;
	T->a[T->size].i=i;
	T->a[T->size].j=j;
	T->a[T->size].value=n;
}
void Myprint(Tmatrix *T)
{
	printf("%d %d %d\n",T->imax,T->jmax,T->size);
	for(int i=1;i<=T->size;i++)
	{
		printf("%d %d %d\n",T->a[i].i,T->a[i].j,T->a[i].value);
	}
}

void Add(Tmatrix *A,Tmatrix *B,Tmatrix *C)
{
	int i=1,j=1;//此ij非彼ij 
	while(i<=A->size&&j<=B->size)//遍历AB数组
	{
		if(A->a[i].i<B->a[j].i)//A数组的第i个结点行数靠前,则先进入C组
		{
			insert_T(C,A->a[i].i,A->a[i].j,A->a[i].value);
			i++;
		}
		else if(A->a[i].i==B->a[j].i)//行相同,下面比较列
		{
			if(A->a[i].j<B->a[j].j)//A[i]列数靠前,先入C组
			{
			    insert_T(C,A->a[i].i,A->a[i].j,A->a[i].value);
			    i++;	
			} 
			else if(A->a[i].j>B->a[j].j)//反之亦然
			{
				insert_T(C,B->a[j].i,B->a[j].j,B->a[j].value);
			    j++;
			}
			else 
			{
				if(A->a[i].value+B->a[j].value!=0) insert_T(C,B->a[j].i,B->a[j].j,B->a[j].value+A->a[i].value);//value值相加为0则删除
				i++;j++;
			}
			 
		}
		else 
		{
			insert_T(C,B->a[j].i,B->a[j].j,B->a[j].value);
			j++;
		}
		
	}
	while(i<=A->size)//处理剩余的结点
	{
		insert_T(C,A->a[i].i,A->a[i].j,A->a[i].value);
		i++;
	}
	while(j<=B->size)
	{
		insert_T(C,B->a[j].i,B->a[j].j,B->a[j].value);
		j++;
	}
}
int main ()
{
	Tmatrix A,B,C;

	int n1,m1,t1;
	int n2,m2,t2;
	//int count=0;
	
	for(int i=1;i<=50000;i++)//初始化 
	{
		A.a[i].value=0;
		B.a[i].value=0;
		C.a[i].value=0;
	}
	cin>>n1>>m1>>t1;
	initial(&A,n1,m1);
	for(int i=1;i<=t1;i++)
	{
		A.size++;
		scanf("%d %d %d",&A.a[i].i,&A.a[i].j,&A.a[i].value);
	}
	cin>>n2>>m2>>t2;
	initial(&B,n2,m2);
	for(int i=1;i<=t2;i++)
	{
		B.size++;
		scanf("%d %d %d",&B.a[i].i,&B.a[i].j,&B.a[i].value);
	}
	
	if(n1!=n2||m1!=m2)//不合法 
	{
		cout<<"Illegal!";
		return 0;
	}
	
	initial(&C,n1,m1);
	Add(&A,&B,&C);
	
	
	Myprint(&C);
	
	
	
} 

三、7-3文字编辑

题干:

一篇文章由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(n²)级别的时间复杂度,故,即使课下认真debug,也只过了前面三个点。

但课后老师提示了可以通过跳舞链来实现,一顿饿补之后,就有了下面的代码。

跳舞链

跳舞链,在我看来,可以算是一种静态双向循环链表(理解可能有些偏差)
跳舞链的删除,其实并没有改变该元素头尾指针,存在风险,但,我们正利用了这个风险,使插入时,更加简便。

部分代码如下:

void creat(int n)
{
	for(int i=1;i<=n;i++)
	{
		//a[i]=i;
		l[i]=i-1;
		r[i]=i+1;
	}
	r[n]=1;
	l[1]=n;
 } 
 
//下面为“摘下”操作
void del_A(int i,int j)//删除i元素,并改变i的左右指针以便插入j前 
 {
 	r[l[i]]=r[i];
 	l[r[i]]=l[i];
 	r[i]=j;
 	l[i]=l[j];
 }
void del_B(int i,int j)//删除i元素,并改变i的左右指针以便插入j后 
 {
 	r[l[i]]=r[i];
 	l[r[i]]=l[i];
 	l[i]=j;
 	r[i]=r[j];
 }
 
void ins(int x)//利用风险,在新位置挂上
 {
 	r[l[x]]=x;
 	l[r[x]]=x;
 }

完整代码如下:

至于ABQ是怎么执行的,其实有了跳舞链,就只是一删与一插的事。

#include<iostream>
#include<string>
using namespace std;
//int a[10001];//汉字
int l[10001];//左索引
int r[10001];//右索引
void creat(int n)
{
	for(int i=1;i<=n;i++)
	{
		//a[i]=i;
		l[i]=i-1;
		r[i]=i+1;
	}
	r[n]=1;
	l[1]=n;
 } 
void del_A(int i,int j)//删除i元素,并改变i的左右指针以便插入j前 
 {
 	r[l[i]]=r[i];
 	l[r[i]]=l[i];
 	r[i]=j;
 	l[i]=l[j];
 }
void del_B(int i,int j)//删除i元素,并改变i的左右指针以便插入j后 
 {
 	r[l[i]]=r[i];
 	l[r[i]]=l[i];
 	l[i]=j;
 	r[i]=r[j];
 }
 
void ins(int x)
 {
 	r[l[x]]=x;
 	l[r[x]]=x;
 }
void func(char s[],int i,int j,int n)
{
	if(s[0]=='A')
	{
		del_A(i,j);
		ins(i);
	}else
	if(s[0]=='B')
	{
		del_B(i,j);
		ins(i);
	}else
	if(s[0]=='Q')
	{
		if(i==0)
		{
			printf("%d\n",l[j]);
		}
		else if(i==1)
		{
			printf("%d\n",r[j]);
		}
	}
}

int main()
{
	int T;cin>>T;
	int n,m;
	char s[3];
	int x,y;
	for(int i=1;i<=T;i++)
	{
	scanf("%d%d",&n,&m);
	creat(n);
	for(int i=1;i<=m;i++)
	{
		scanf("%s%d%d",s,&x,&y);
	//	cout<<s<<x<<y<<endl;
		func(s,x,y,n);
	}
	}

	
}

四、7-4幸福指数

题干

人生中哪段时间最幸福?幸福指数可能会帮你发现。幸福指数要求:对自己每天的生活赋予一个幸福值,幸福值越大表示越幸福。一段时间的幸福指数就是:这段时间的幸福值的和乘以这段时间的幸福值的最小值。幸福指数最大的那段时间,可能就是人生中最幸福的时光。

输入格式:
第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

题目限制

思路分析:

不得不说,代码这方面,向大佬们学习还是有点用的,从大佬习得每个i可以对应一个最大幸福指数区间的思想,这样保证了外层是O(n)级别的时间复杂度。又通过对单调栈的学习,对区间的单调划分,也达到了O(n)级别,受益匪浅!
具体思路,将在代码中体现

代码如下:

#include<iostream>
#include<stack>
using namespace std;
long long int now=0;
long long int max_now= 0;
stack<int>lstack;//左边界栈
stack<int>rstack;//右边界栈
long long int a[100001];//单个值的值 
long long int sum[100001];//从1到n区间的和值 
long long int l[100001], r[100001];//存储各个值对应的左右区间 
long long int L, R;//最终左右端点 
int main()
{
	int n;
	scanf("%d", &n);
	a[0] = a[n + 1] = -1;//设置左右端点为必定最小值 

	for (int i = 1; i <= n; i++)//求前缀和 
	{
		scanf("%d", &a[i]);
		sum[i] = sum[i - 1] + a[i];
	}
	lstack.push(0);//入栈,入数组下标
	for (int i = 1; i <= n; i++)//遍历i的左区间,用单调栈找到小于i的数 
	{
		while (a[lstack.top()] >= a[i])//小于栈顶,非空则弹栈
		{
			if (lstack.empty()) break;//空则结束
			else lstack.pop();//非空则弹栈至大于栈顶
		}
		l[i] = lstack.top();//该值对应下标i的左区间数组更新为栈顶元素
		lstack.push(i);//该值下标i入栈
	}
	rstack.push(n + 1);
	for (int i = n; i >= 1; i--)//遍历i的右区间,用单调栈找到小于i的数 
	{
		while (a[rstack.top()] >= a[i])//同上
		{
			if (rstack.empty())break;
			else rstack.pop();
		}
		r[i] = rstack.top();
		rstack.push(i);
	}
	for (int i = 1; i <= n; i++)//遍历n个区间,更新max求最大
	{
		now = (sum[r[i] - 1] - sum[l[i]]) * a[i];
		if (now > max_now)//now大则更新
		{
			max_now = now;
			L = l[i];
			R = r[i];
		}
		else if (now == max_now && R - L < r[i] - 1 - l[i] - 1)//相等,则取最左最大
		{
			R = r[i];
			L = l[i];
		}
		else if (now == max_now && l[i]+1< L)//同上
		{
			R = r[i];
			L = l[i];
		} 
	}
	//for(int i=1;i<=n;i++) cout<<l[i]<<" ";
	//cout<<endl;
	//for(int i=1;i<=n;i++) cout<<r[i]<<" ";
	printf("%lld\n%lld %lld\n",max_now,L+1,R-1);
}

后面注释掉的三行,用于调试两个栈是否出现错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值