2021-05-16

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

思路:根据题目我们就知道这个题是一个典型的递归题,但是我们也很明白递归就意味着要超时,所以我们就要考虑循环,但是我们看到查询次数10000次所以循环我们应该也是会超时的,所以我们也会想到只做一次来将其结果保存即可。实际上我们的最终结果也是这样的。那接下来我们就来看下代码吧。

#include<iostream>
using namespace std;
int a[50000];

int main() {
	int k, j, i, l;
	a[1] = 10;
	for (i = 2;i < 50000;++i) {
		a[i] = 11 * a[i - 1] / 10;
	}
	scanf("%d", &k);
	for (i = 0;i < k-1;++i)
	{
		scanf("%d", &j);
		printf("%d\n", a[j]);
		
	}
	scanf("%d", &j);
	printf("%d", a[j]);

	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

思路:先说一下本人的思路这个是错的可以不用看这部分用括号标注(本人想着用一个map来排序和查找,即将一个行列结构体作为key值,value即为对应的值,每次假如是都要寻找一次看是否有这个行列出现如果出现就将其value和输入的value相加加入即可注意相加时可能为0这个时候要将其删除,另外我们还要用一个计数器来记录其中的个数,如果没出现则将其填入即可,总体时间复杂度时n*logn,刚刚不能过,哈哈)那接下来我们就讲讲正解吧。我们这次可以将其对应的读入到两个三元组结构体中,我们注意到题目给的要求给出的已经是按照默认行列排好序的。所以第一个我们保存,第一个三元组,第二个我们保存第二个三元组,我们再开一个三元组来实现其相加和。这种方法时n的具体不多说了我们看代码。

#include <bits/stdc++.h>

using namespace std;
class the{             //设置一个三元组结构体。
    public:
    int row;
    int lie;
    int value;
};
the a[50001],b[50001],c[50001];int top;
int main()
{
    int i,j,k0,k1,l,m0,n0,m1,n1;
    scanf("%d%d%d",&m0,&n0,&k0);
    for(i=0;i<k0;i++){
     scanf("%d%d%d",&a[i].row,&a[i].lie,&a[i].value);//读入第一个
    }
    scanf("%d%d%d",&m1,&n1,&k1);
    if(m0!=m1||n0!=n1){                     //不能相加输出后退出
        printf("Illegal!");
        return 0;
    }
    for(i=0;i<k1;++i){
        scanf("%d%d%d",&b[i].row,&b[i].lie,&b[i].value);//读入第二个
    }
    int top0=0,top1=0;
    while(top0<k0&&top1<k1){             //循环到其中一个读取完毕
        if(a[top0].row<b[top1].row){       //判断第一个数组和第二个数组对应的顺序来进行加减
            c[top]=a[top0];
            top++;top0++;
            continue;
        }
        if(a[top0].row>b[top1].row){
            c[top]=b[top1];top1++;
            top++;continue;
        }
            if(a[top0].lie<b[top1].lie){
            c[top]=a[top0];top0++;
            top++;
            continue;
        }
        if(a[top0].lie>b[top1].lie){
            c[top]=b[top1];top1++;
            top++;continue;
        }
        c[top].row=a[top0].row;
        c[top].lie=a[top0].lie;
        c[top].value=a[top0].value+b[top1].value;
        top0++;top1++;
        if(c[top].value){
            top++;continue;
        }
    }
    if(top0==k0){
        while(top1<k1){
            c[top++]=b[top1++];
        }
    }
    if(top1==k1){
        while(top0<k0){
            c[top++]=a[top0++];
        }
    }
    printf("%d %d %d",m0,n0,top);//输出。
    for(i=0;i<top;i++){
        printf("\n%d %d %d",c[i].row,c[i].lie,c[i].value);
    }
    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个整数,对应每个询问的结果汉字编号。

输入样例:

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

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

输出样例:

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

4
9998

思路:一看这个题就是要卡时间的,所以我们知道数组的查询是O(1),修改为值则是O(n),而链表的查询是O(n),但是其修改则是O(1),所以我们就要思考将两者结合起来,形成一个链式数组。即一个结构体,其中包含着一个前驱和一个后继,那么我们每次操作就是O(1)的是很好的。我们每次修改就修改他的前驱和后继即可。那么我们就来看代码。可以先参考跳舞链。

#include <iostream>
using namespace std;
class {               //设置一个链式数组
public:
    int fr;            
    int ne;

}a[10000];            //用下标代替其值
int c0[10000], top;
inline void chu(int k) {        //这里采用inline提高效率对其进行初始化

    a[1].fr = k;a[1].ne = 2;
    a[k].ne = 1;a[k].fr = k - 1;
    for (int i = 2;i < k;i++) {
        a[i].fr = i - 1;
        a[i].ne = i + 1;
    }
}
int main() {
    int i, j, k, zu, co, m, n;
    char c;
    scanf("%d", &zu);
    for (i = 0;i < zu - 1;++i) {
        scanf("%d%d", &k, &co);
        chu(k);
        for (j = 0;j < co;++j) {
            scanf("%c", &c);
            while (c != 'A' && c != 'B' && c != 'Q')c = getchar();
            scanf("%d%d", &m, &n);
            switch (c) {
            case 'A':a[a[m].fr].ne = a[m].ne;a[a[m].ne].fr = a[m].fr; a[a[n].fr].ne = m;a[m].fr = a[n].fr;a[m].ne = n;a[n].fr = m;break;     //换位操作
            case 'B':a[a[m].fr].ne = a[m].ne;a[a[m].ne].fr = a[m].fr;a[m].ne = a[n].ne;a[a[n].ne].fr = m;a[n].ne = m;a[m].fr = n;break;//换位
            case 'Q':if (m)printf("%d\n", a[n].ne);else printf("%d\n", a[n].fr);break;//查询
            }
        }
       
    } scanf("%d%d", &k, &co);
    chu(k);  //再进行双循环是为了使其输出格式的换行与题目保持一致
    for (j = 0;j < co;++j) {
        c = getchar();
        while (c != 'A' && c != 'B' && c != 'Q') c=getchar();
        scanf("%d%d", &m, &n);
        switch (c) {
        case 'A':a[a[m].fr].ne = a[m].ne;a[a[m].ne].fr = a[m].fr; a[a[n].fr].ne = m;a[m].fr = a[n].fr;a[m].ne = n;a[n].fr = m;break;
        case 'B':a[a[m].fr].ne = a[m].ne;a[a[m].ne].fr = a[m].fr;a[m].ne = a[n].ne;a[a[n].ne].fr = m;a[n].ne = m;a[m].fr = n;break;
        case 'Q':if (m)c0[top++] = a[n].ne;else c0[top++] = a[n].fr;break;
        }
    }
    for (i = 0;i < top - 1;++i) {
        printf("%d\n", c0[i]);
    }
    printf("%d", c0[i]);
}

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

思路:上一次测试我们用了单调队列,这次我们怎能不靠单调栈?由于我们要找这段区间里的最小值与这段区间和的乘积,所以我们每次都先选中一个点让其作为最小值,寻找该区间的最大值,由于题目要的是最大值而各个数字都是大于等于0,所以只用找到以比这个值左边更小的和右边更小的,即可将这段区间的和乘于该值即可。由于我们的单调栈的弹出时将其按照比当前大的数字弹出即可所以我们的弹出到一个不能弹出的我们就将其,的下标记录下来我们就接着进行即可。

单调栈的作用:
可以以 O(1) 的时间复杂度得知某个位置左右两侧比他大(或小)的数的位置,当你需要高效率获取某个位置左右两侧比他大(或小)的数的位置的的时候就可以用到单调栈。

求解数组中元素右边第一个比它小的元素的下标,从前往后,构造单调递增栈;
求解数组中元素右边第一个比它大的元素的下标,从前往后,构造单调递减栈;
求解数组中元素左边第一个比它小的元素的下标,从后往前,构造单调递减栈;
求解数组中元素左边第一个比它小的元素的下标,从后往前,构造单调递增栈。

另外我们要注意此题要用long long本人就被他卡了。

那接下来让我们看代码吧。

#include <bits/stdc++.h>

using namespace std;
    stack<long long>p;
    long long a[1000010],b[1000010];
int main(){
    long long m,n,j,i,k,top,tmp=-1,lef,right,l0,r0;
   scanf("%lld",&n);
   scanf("%lld",&a[1]);
   b[1]=a[1];
   for(i=2;i<=n;i++){
    scanf("%lld",a+i);
    b[i]=b[i-1]+a[i];        //a记录其值,b记录其前缀和
   }
   a[i]=-1;                 //此处设置-1是为了让栈中元素全部都出栈。
   b[i]=b[i-1]-1;
   for(i=1;i<=n+1;++i){//逐次加入
    if(p.empty()||a[p.top()]<=a[i]){  //如果a[i]<=a[top]我们就让a[i]入栈。我们建立的是一个单调递增栈。
        p.push(i);
    }else{
        while(!p.empty()&&a[p.top()]>a[i]){  //出栈
        top=p.top();                         
        p.pop();
        k=a[top]*(b[i-1]-b[top-1]);//a[top]并非是刚开始输入的a[top]而是修改后的我们这个地方是很巧妙地由于这个a[top]是在top和i-1区间里的最小值。b[i-1]-b[top-1]即是这段区间的和。
        if(k>tmp){       //求最大值
            lef=top;right=i-1;
            tmp=k;
        }
        if(k==tmp){
                l0=i-1-top;
                r0=right-lef;
            if(l0>r0) //求最长区间
            {

            lef=top;right=i-1;}
            if(l0==r0)
            if(lef>top)//求最左区间
                 {

            lef=top;right=i-1;}
        }
        }
         p.push(top);
         a[top]=a[i];//由于a[i]比a[top]小所以我们就将a[top]转换为其对应的a[i]。保证后续操作为前边区间的最小值。
    }

   }
   printf("%lld\n%lld %lld",tmp,lef,right);
    return 0;

}

代码可能转换哪里可能会有些难理解,其实仔细理解理解还是能看懂的。

总结:这次的测试,相对来说也不是太难但是本人就是菜。就做对了第一道,但是也收获了很多,例如单调栈,数组链,还有三元组表,相信自己将会在接下来的打击中,越战越勇,加油。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值