2021-05-23

2020级荣誉课第二次上机实验

一、数列查询

题意:通过通项公式,快速求出要查询的f(n)值。

思路:这道题看似简单,但仔细观察时间限制竟然只有10ms,首先尝试暴力的边输入便求解,结果超时,超时原因有多种,一是如果用c++未解绑的输入输出流的话会很慢,二是有重复查询的话,重复计算,浪费时间,不妨先将查询的n值上限在输入时求出赋予max,再在后面将一系列f(n)值(n<=m)求出,最后查询时既不会重复计算,且查询时间为o(1)。

1)、输入输出流解绑:

    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    //给输入输出流解绑,使其达到scanf和printf的速度

2)、关键代码:

for(i=1;i<=q;i++){
    cin>>a[i];
    if(max<a[i]) max=a[i];
    }
    f[1]=10;
    for(i=1;i<=max;i++){
       f[i+1]=f[i]*11/10;
    }

3)、完整代码:

#include<stdio.h>
#include<stack>
#include<iostream>
using namespace std;

int f[20000];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    //给输入输出流解绑,使其达到scanf和printf的速度
    int q,max=0;
    int i,a[20000];
    cin>>q;
    for(i=1;i<=q;i++){
    cin>>a[i];
    if(max<a[i]) max=a[i];
    }
    f[1]=10;
    for(i=1;i<=max;i++){
       f[i+1]=f[i]*11/10;
    }
    for(i=1;i<=q;i++){
       cout<<f[a[i]]<<"\n";
    }
}

二、稀疏矩阵求和

题意:稀疏矩阵用三元组保存,实现加法运算。

思路:稀疏矩阵三元组存储只存储非零元素以及该矩阵行列数,我们可以用一个结构体数组存储非零元素以及其所在的行数和列数,在做加法时对其非零元素值和数组有效元素的位置和数目进行调整。

在进行加法时首先应判断两矩阵是否可加,后判断两矩阵是否有行数或列数不相等的元素,若有需要往数组加入该元素,并根据行列数调整其位置。方便以后输出。

1)、存储方式

typedef struct{
	int i,j,v;     //行、列、数值
}node;
typedef struct{
	node data[max];
	int m,n,x;      //稀疏矩阵行数、列数及非0元个数
}triple;

2)、建立三元组表:

triple * Setmatrix(){    //建三元组表
	triple *S;
	S=(triple *)malloc(sizeof(triple));

	scanf("%d%d%d",&S->m,&S->n,&S->x);

	for(int n=0;n<S->x;n++)
		scanf("%d%d%d",&S->data[n].i,&S->data[n].j,&S->data[n].v);
	return S;
}

三)关键代码:矩阵加法

triple *Matrixadd(triple *A,triple *B){    //两稀疏矩阵相加
	if(A->m!=B->m||A->n!=B->n)
	{
	    printf("Illegal!");
	    return 0;
	}
	triple *C;
	C=(triple *)malloc(sizeof(triple));
	C->m=A->m;
	C->n=A->n;
	C->x=0;
	int a,b,e,k;
	a=b=e=0;
	for(k=1;k<=A->m;k++){
		while(A->data[a].i==k&&B->data[b].i==k){    //两结点属于同一行
			if(A->data[a].j<B->data[b].j){//A中下标a的结点小于B中下标为b的结点的列数
				C->data[e].v=A->data[a].v;
				C->data[e].i=A->data[a].i;
				C->data[e].j=A->data[a].j;
				e++;
				a++;
			}
			else if(A->data[a].j==B->data[b].j){    //两结点属于同一列
				int temp=A->data[a].v+B->data[b].v;
				if(temp==0){        //两结点数值相加等于0
					a++;
					b++;
				}
				else{       //两结点数值相加不等于0
					C->data[e].v=temp;
					C->data[e].i=A->data[a].i;
					C->data[e].j=A->data[a].j;
					e++;
					a++;
					b++;
				}
			}
			else{          //A中下标a的结点大于B中下标为b的结点的列数
				C->data[e].v=B->data[b].v;
				C->data[e].i=B->data[b].i;
				C->data[e].j=B->data[b].j;
				e++;
				b++;
			}
		}
		while(A->data[a].i==k){     //只有A中剩下行数为k的未处理结点
			C->data[e].v=A->data[a].v;
			C->data[e].i=A->data[a].i;
			C->data[e].j=A->data[a].j;
			e++;
			a++;
		}
		while(B->data[b].i==k){    //只有B中剩下行数为k的未处理结点
			C->data[e].v=B->data[b].v;
			C->data[e].i=B->data[b].i;
			C->data[e].j=B->data[b].j;
			e++;
			b++;
		}
	}
	C->x=e-1;
	return C;
}

四)、完整代码

#include<stdio.h>
#include<stdlib.h>
#define max 900000
#define zero 0
typedef struct{
	int i,j,v;     //行、列、数值
}node;
typedef struct{
	node data[max];
	int m,n,x;      //稀疏矩阵行数、列数及非0元个数
}triple;
triple * Setmatrix(){    //建三元组表
	triple *S;
	S=(triple *)malloc(sizeof(triple));

	scanf("%d%d%d",&S->m,&S->n,&S->x);

	for(int n=0;n<S->x;n++)
		scanf("%d%d%d",&S->data[n].i,&S->data[n].j,&S->data[n].v);
	return S;
}
void tripleout(triple *S){    //输出矩阵
	printf("%d %d %d\n",S->m,S->n,S->x+1);
	int a,b,e,sum;
	e=0;
	for(int i=0;i<S->x+1;i++)
    {
        printf("%d %d %d",S->data[i].i,S->data[i].j,S->data[i].v);
        if(i<S->x) printf("\n");
    }
}

triple *Matrixadd(triple *A,triple *B){    //两稀疏矩阵相加
	if(A->m!=B->m||A->n!=B->n)
	{
	    printf("Illegal!");
	    return 0;
	}
	triple *C;
	C=(triple *)malloc(sizeof(triple));
	C->m=A->m;
	C->n=A->n;
	C->x=0;
	int a,b,e,k;
	a=b=e=0;
	for(k=1;k<=A->m;k++){
		while(A->data[a].i==k&&B->data[b].i==k){    //两结点属于同一行
			if(A->data[a].j<B->data[b].j){//A中下标a的结点小于B中下标为b的结点的列数
				C->data[e].v=A->data[a].v;
				C->data[e].i=A->data[a].i;
				C->data[e].j=A->data[a].j;
				e++;
				a++;
			}
			else if(A->data[a].j==B->data[b].j){    //两结点属于同一列
				int temp=A->data[a].v+B->data[b].v;
				if(temp==0){        //两结点数值相加等于0
					a++;
					b++;
				}
				else{       //两结点数值相加不等于0
					C->data[e].v=temp;
					C->data[e].i=A->data[a].i;
					C->data[e].j=A->data[a].j;
					e++;
					a++;
					b++;
				}
			}
			else{          //A中下标a的结点大于B中下标为b的结点的列数
				C->data[e].v=B->data[b].v;
				C->data[e].i=B->data[b].i;
				C->data[e].j=B->data[b].j;
				e++;
				b++;
			}
		}
		while(A->data[a].i==k){     //只有A中剩下行数为k的未处理结点
			C->data[e].v=A->data[a].v;
			C->data[e].i=A->data[a].i;
			C->data[e].j=A->data[a].j;
			e++;
			a++;
		}
		while(B->data[b].i==k){    //只有B中剩下行数为k的未处理结点
			C->data[e].v=B->data[b].v;
			C->data[e].i=B->data[b].i;
			C->data[e].j=B->data[b].j;
			e++;
			b++;
		}
	}
	C->x=e-1;
	return C;
}
int main(){
	triple *A,*B,*C;
	A=Setmatrix();
	B=Setmatrix();
if(A->m!=B->m||A->n!=B->n)
{
    printf("Illegal!");
    return 0;
}
	C=Matrixadd(A,B);
	tripleout(C);
}

三、文字编辑

题意:对已编号的文本进行移动查询操作。

思路:本题最容易想到的是用链表存储,并进行节点的插入,遍历访问以及等操作。但题目中内存限制为2M,且有多组测试数据,需要大量空间,于此同时空间的申请,以及为了节省系统空间不断释放链表,会占用大量时间,最后也无法拿到满分。所以我们采用静态链表,我们申请三个数组,a数组存数据域,next数组存下一个节点坐标(地址),pre数组存其前驱节点。这样我们无论是数据移动还是访问的时间都能做到o(1)。

1)、静态链表初始化

for(int sum=0;sum<T;sum++)
    {    int a[10005],next[10005],pre[10005];
        int b[200000],num=0;
        int x,y;
        cin>>x>>y;
        int m=y;

        for(int i0=1;i0<=x;i0++)
        {
            a[i0]=i0;
            if(i0==x) next[x]=1;
            else next[i0]=i0+1;
            if(i0==1) pre[1]=x;
            else pre[i0]=i0-1;
        }

2)、跳舞操作(数据移动)

 if(ch=='A')
            {
                next[pre[i]]=next[i];
                pre[next[i]]=pre[i];
                pre[i]=pre[j];
                next[i]=j;
                next[pre[j]]=i;
                pre[j]=i;
            }
            else if(ch=='B')
            {
               next[pre[i]]=next[i];
               pre[next[i]]=pre[i];
               next[i]=next[j];
               pre[i]=j;
               pre[next[j]]=i;
                next[j]=i;

            }

3)、完整代码

#include <iostream>
#include<malloc.h>
#include<cstdio>
using namespace std;
int main()
{  
    int T;
    cin>>T;
    for(int sum=0;sum<T;sum++)
    {    int a[10005],next[10005],pre[10005];
        int b[200000],num=0;
        int x,y;
        cin>>x>>y;

        int m=y;

        for(int i0=1;i0<=x;i0++)
        {
            a[i0]=i0;
            if(i0==x) next[x]=1;
            else next[i0]=i0+1;
            if(i0==1) pre[1]=x;
            else pre[i0]=i0-1;

        }
          y=m;

        for(int j0=0;j0<y;j0++)
        {  char ch;
            int i,j;
            cin>>ch>>i>>j;
            if(ch=='A')
            {
                next[pre[i]]=next[i];
                pre[next[i]]=pre[i];
                pre[i]=pre[j];
                next[i]=j;
                next[pre[j]]=i;
                pre[j]=i;
            }
            else if(ch=='B')
            {
               next[pre[i]]=next[i];
               pre[next[i]]=pre[i];
               next[i]=next[j];
               pre[i]=j;
               pre[next[j]]=i;
                next[j]=i;

            }
            else if(ch=='Q')
            {
                if(i==0)
                {

                    b[num++]=a[pre[j]];

                }
                else if(i==1)
                {

                    b[num++]=a[next[j]];
                }

            }

        }

 for(int i=0;i<num;i++)
    {
        printf("%d",b[i]);
        if(i<num-1) printf("\n");
    }
printf("\n");
    
    }
   
return 0;
}

 

 

四、幸福指数

题意:求一段时间内最大幸福指数以及其对应的区间。若有多个,则求最长最左区间。

思路:

1)、暴力枚举,时间复杂度为o(n2),会超时。

2)、单调栈,达到o(n):

仔细读题你会发现这是由一道经典单调栈问题改编的——利用单调栈求最大矩形面积

单调栈最常用到的性质就是求左边或右边第一个比他小的元素。

3)、
知识点:
单调栈。维护一个单调递增的栈,使
用当前元素维护单调性后,栈顶即为所求
1.单调性的维护:当前元素和栈顶元素比较,若栈顶
元素大于当前元素,栈顶元素出栈;重复处理,直
到栈空或栈顶元素小于当前元素;
while(top!=-1 && s[top] > a[i] ) top--;
2.若栈不空,栈顶元素即为所求
3.当前元素入栈

代码:

1)、存储求和sum(1)—sum(n),方便后续计算

for(int i=1;i<=n;i++)
    {
        cin>>h[i];
        sum[i]=sum[i-1]+h[i];
    }

2)、利用单调栈求出每个值左边第一个和右边第一个比它小的元素值并分别将这两个值存在数组lfm[i],rfm[i]中。

 s.push(0);
    for(int i=1;i<=n;i++)
    {
        while(!s.empty()&&h[s.top()]>=h[i]) s.pop();
        lfm[i]=s.top();
        s.push(i);
    }
    while(!s.empty())s.pop();
    s.push(n+1);
    for(int i=n;i>=1;i--)
    {
        while(!s.empty()&&h[s.top()]>=h[i]) s.pop();
        rfm[i]=s.top();
        s.push(i);
    }

3)、求最大幸福指数;

 long long int t=0;
    max0=0;
    for(int i=1;i<=n;i++)
    {

        t=(sum[rfm[i]-1]-sum[lfm[i]])*h[i];
        if(t>max0)
        {
            max0=t;
            sectionl=lfm[i]+1;
            sectionr=rfm[i]-1;

        }
        else if(t==max0)
        {
         if(sectionr- sectionl<rfm[i]-lfm[i]-2)
            {
                sectionl=lfm[i]+1;
                sectionr=rfm[i]-1;
            }
        else if(lfm[i]+1<sectionl)
        {
                sectionl=lfm[i]+1;
                sectionr=rfm[i]-1;
        }

        }

    }

4)、完整代码:

#include <iostream>
#include <stack>
int  edls=100000000;
const int  maxsize=2e+5;
using namespace std;
long long int sum[maxsize];
int h[maxsize],lfm[maxsize],rfm[maxsize];
long long int max0,sectionl,sectionr;
stack <int> s;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    //给输入输出流解绑,使其达到scanf和printf的速度

    int n;
    cin>>n;
    h[0]=h[n+1]=-1000005;
    for(int i=1;i<=n;i++)
    {
        cin>>h[i];
        sum[i]=sum[i-1]+h[i];
    }
    s.push(0);
    for(int i=1;i<=n;i++)
    {
        while(!s.empty()&&h[s.top()]>=h[i]) s.pop();
        lfm[i]=s.top();
        s.push(i);
    }
    while(!s.empty())s.pop();
    s.push(n+1);
    for(int i=n;i>=1;i--)
    {
        while(!s.empty()&&h[s.top()]>=h[i]) s.pop();
        rfm[i]=s.top();
        s.push(i);
    }
    long long int t=0;
    max0=0;
    for(int i=1;i<=n;i++)
    {

        t=(sum[rfm[i]-1]-sum[lfm[i]])*h[i];
        if(t>max0)
        {
            max0=t;
            sectionl=lfm[i]+1;
            sectionr=rfm[i]-1;

        }
        else if(t==max0)
        {
         if(sectionr- sectionl<rfm[i]-lfm[i]-2)
            {
                sectionl=lfm[i]+1;
                sectionr=rfm[i]-1;
            }
        else if(lfm[i]+1<sectionl)
        {
                sectionl=lfm[i]+1;
                sectionr=rfm[i]-1;
        }

        }

    }

 cout<<max0<<"\n"<<sectionl<<" "<<sectionr;


}

 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值