数据结构第二次上机实验

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

题目分析

即考察记忆化搜索。

代码实现如下

#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;
} 

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,则舍弃该节点。

代码实现如下

 

#include<stdio.h>
#include<stdlib.h>
typedef struct ju{
	int r,c,v;
}ju;
	ju a[50010],b[50010],c[110000];
int main(){
	int Ma,Na,ta,Mb,Nb,tb,topa=0,topc=0,flag=1;
	scanf("%d %d %d",&Ma,&Na,&ta);
	for(int i=0;i<ta;i++){
		scanf("%d %d %d",&a[i].r,&a[i].c,&a[i].v);
	}
	
	scanf("%d %d %d",&Mb,&Nb,&tb);
	for(int i=0;i<tb;i++){
		scanf("%d %d %d",&b[i].r,&b[i].c,&b[i].v);
		if(topa!=ta){
			if(a[topa].r==b[i].r&&a[topa].c==b[i].c){
				c[topc].r=a[topa].r;
				c[topc].c=a[topa].c;
				c[topc].v=a[topa].v+b[i].v;
				topa++;
				topc++;
                if(c[topc-1].v==0){
                    topc--;
                }
				continue;
				}
			else
				while(topa!=ta){
					if(a[topa].r<b[i].r){
						c[topc].r=a[topa].r;
						c[topc].c=a[topa].c;
						c[topc].v=a[topa].v;
						topa++;
						topc++;
						}
					else if(a[topa].r==b[i].r&&a[topa].c<b[i].c){
						c[topc].r=a[topa].r;
						c[topc].c=a[topa].c;
						c[topc].v=a[topa].v;
						topa++;
						topc++;
					}
					else break;
					}
			
			
			}
		c[topc].r=b[i].r;
		c[topc].c=b[i].c;
		c[topc].v=b[i].v;
		topc++;
	}
    while(topa!=ta){
        c[topc].r=a[topa].r;
	    c[topc].c=a[topa].c;
		c[topc].v=a[topa].v;
		topa++;
		topc++;
    }
	if(Ma!=Mb||Na!=Nb){printf("Illegal!");return 0;}
	printf("%d %d %d\n",Ma,Na,topc);
	for(int i=0;i<topc;i++){
		printf("%d %d %d\n",c[i].r,c[i].c,c[i].v);
	}
	return 0;
} 

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个整数,对应每个询问的结果汉字编号。

输入样例:

在这里给出一组输入。例如:


9999 4 
B 1 2  
A 3 9999
Q 1 1
Q 0 3

输出样例:

在这里给出相应的输出。例如:

4
9998

题目分析

我手写了一个双端队列,每次操作更改前驱后继即可。

代码实现如下

#include<stdio.h>
struct po{
	int fo,next;
}a[10000];
int Q(struct po *a,int k,int i){
	if(k==1)return a[i].next;
	else if(k==0)return a[i].fo;
}
void take_front(struct po *a,int i,int j){
	int r,t,k,n;
    if(a[i].next==j)return;
	if(a[j].next!=i){
        r=a[i].fo;
	    k=a[i].next;
	    n=a[j].fo;
        t=a[j].next;
        //
	    a[r].next=k;
	    a[k].fo=r;
        //
	    a[n].next=i;
	    a[i].fo=n;
	    a[j].fo=i;
	    a[i].next=j;
    }
    else {
        k=a[i].next;
	    n=a[j].fo;
        //
        a[k].fo=j;
        a[j].next=k;
        a[j].fo=i;
        a[i].next=j;
        a[i].fo=n;
        a[n].next=i;
    }
    
	return ;
}
void take_back(struct po *a,int i,int j){
	int r,t,k,n;
    if(a[j].next==i)return;
	if(a[i].next!=j){
        r=a[i].fo;
	    k=a[i].next;
	    t=a[j].next;
        //
	    a[r].next=k;
	    a[k].fo=r;
        //
	    a[i].fo=j;
	    a[j].next=i;
	    a[i].next=t;
	    a[t].fo=i;
    }
    else {
        r=a[i].fo;
        t=a[j].next;
        //
        a[r].next=j;
        a[j].fo=r;
        a[i].fo=j;
        a[j].next=i;
        a[i].next=t;
        a[t].fo=i;
    }
	return ;
}
int main(){
	int t,n,m,i,j,x,y;
	char ran;
	scanf("%d",&t);
	for(i=0;i<t;i++){
		scanf("%d %d",&n,&m);
		a[n].next=1;a[1].fo=n;
		for(j=1;j<n;j++){a[j].next=j+1;a[j+1].fo=j;}
		for(j=0;j<m;j++){
			scanf("\n%c%d%d",&ran,&x,&y);
			if(ran=='Q')printf("%d\n",Q(a,x,y));
			else if(ran=='A')take_front(a,x,y);
			else if(ran=='B')take_back(a,x,y);
		}
	}
	return 0;
} 

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种情况:每一个值作为区间最小值,计算区间和与之的乘积,得数最大的就是所求区间。使用一个单调栈,求出每个以该值最为最小值的区间的左边界与右边界即可。

代码实现如下

#include <stdio.h>
int a[100010];
long long b[100010];
int l[100010],r[100010];
int queuel[100010],queuer[100010];
int INF = 1e9;
int main(){
    int n,i,k,topl=0,topr=0;
    scanf("%d",&n);
    a[0]=a[n+1]=-INF;
    for(i=1;i<=n;i++){
        scanf("%d",&k);
        a[i]=k;
        b[i]=a[i]+b[i-1];
        }
    queuel[0]=0;
    for(i=1;i<=n;i++){
        while(a[queuel[topl]]>=a[i])topl--;
        l[i]=queuel[topl];
        queuel[++topl]=i;
        }
    queuer[0]=n+1;
    for(i=n;i>=1;i--){
        while(a[queuer[topr]]>=a[i])topr--;
        r[i]=queuer[topr];
        queuer[++topr]=i;
        }
    long long max=0;
    int l0,r0;
    for(i=1;i<=n;i++){
        long long t;
        t=a[i]*(b[r[i]-1]-b[l[i]]);
        if(t>max){
            max=t;
            l0=l[i]+1;
            r0=r[i]-1;
            }
        else if(t==max){
            if(r[i]-l[i]-2>r0-l0){
                l0=l[i]+1;
                r0=r[i]-1;
                }
            else if(l[i] + 1 <l0){
                l0=l[i]+1;
                r0=r[i]-1;
                }
            }
        }
    printf("%lld\n%d %d\n",max,l0,r0); 
    return 0;
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值