JLU数据结构荣誉课——第一次实验

JLU数据结构荣誉课——第一次实验

一.7-1 重复计数 (100 分)

在一个有限的正整数序列中,有些数会多次重复出现。请你统计每个数的出现次数,然后按数字在序列中第一次出现的位置顺序输出数及其次数。

  作者               谷方明
  单位               吉林大学
  代码长度限制        16 KB
  时间限制            1000 ms
  内存限制            64 MB

输入格式:

第1行,1个整数N,表示整数的个数,(1≤N≤50000)。

第2行,N个正整数,每个整数x 都满足 1 ≤ x ≤2000000000。

输出格式:

若干行,每行两个用一个空格隔开的数,第一个是数列中出现的数,第二个是该数在序列中出现的次数。

输入样例:

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

12
8 2 8 2 2 11 1 1 8 1 13 13

输出样例:

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

8 3
2 3
11 1
1 3
13 2

题目解析:

记录每一个元素的输入次数,并按照原次序输出元素及其次数

解法一:

由于刚做过哈夫曼树压缩的实验,首先想到的解法是开一个num[2000000001]的int型数组,将输入整数的值作为下标,将次数存入数组。但是因为输出的次序必须和输入的次序保持一致,我么还需要记录输入的次序。于是我想到,用一个a[50000]的int数组依次储存输入的元素,再用一个visited[2000000001]大小的bool数组在输出时遍历判断元素是否已经输出。(经过多次实验,一些编译器可能不会让你开这么大的数组,但是PTA上可以提交 /doge)

具体代码实现如下:
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
int num[2000000000];
long  a[50000],tem;
bool visited[2000000000]={false};
int main(){

	int i,N;
	scanf("%d",&N);
	for(i=0;i<N;i++){
		scanf("%lld",&a[i]);
		visited[a[i]]=true;
		num[a[i]]++;
	}
	for(i=0;i<N;i++){
		if(visited[a[i]]){
			printf("%d %lld\n",a[i],num[a[i]]);
			visited[a[i]]=false;
		}
	}
	return 0;
}


解法二:

第二种解法,也是谷老师说希望我们用的,sort排序。先用sort函数将输入数据排为有序数列,再通过upper_bound和lower_bound求出每一个数字的重复次数,也就是出现次数。对于输入的顺序是通过队列保存,便于输出时使用。至于输入数是否为第一次出现,这是通过 < set > 中的 count功能进行判断。

具体代码实现如下:
#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    set<int>b;
    scanf("%d",&n);
    int a[n+1];
    int q[n+1];//队列,保存原始数据顺序
    int top=0,rear=0;
    int i,t;
    for(i=0;i<n;i++)
    {
        scanf("%d",&t);
        a[i]=t;
        if(b.count(t)==0)
        {
            b.insert(t);
            q[rear++]=t;
        }
    }
    sort(a,a+n);
    while(top!=rear)
    {
        if(top!=0)printf("\n");
        t=q[top++];
        printf("%d %d",t,upper_bound(a,a+n,t)-lower_bound(a,a+n,t));
    }
}

二.7-2 报数游戏 (100 分)

n个人围成一圈,从1开始依次编号,做报数游戏。 现指定从第1个人开始报数,报数到第m个人时,该人出圈,然后从其下一个人重新开始报数,仍是报数到第m个人出圈,如此重复下去,直到所有人都出圈。总人数不足m时将循环报数。请输出所有人出圈的顺序。

  作者               谷方明
  单位               吉林大学
  代码长度限制        16 KB
  时间限制            1000 ms
  内存限制            64 MB

输入样例:

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

5 2

输出样例:

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

2 4 1 5 3

题目解析:

这是约瑟夫环问题。

解法一:

首先呢,我想到的是通过数组实现。

具体代码实现如下:
#include<stdio.h>
int main()
{
    int n,n1,m,i,j=0,k;
    scanf("%d%d",&n,&m);
    int a[n];
    for(i=0;i<n;i++)
        a[i]=i+1;
    n1=n;
    for(i=0;i<n1;i++)
    {
        j=j+m-1;
        j=j%n;
        if(i!=n1-1)
            printf("%d ",a[j]);
        else
            printf("%d",a[j]);
        for(k=j;k<n1-1;k++)
        {
            a[k]=a[k+1];
        }
        n--;
    }
}

由于数组元素位置的移动使他的时间复杂度达到了O(n^2)级别,以至于。。。。。
于是就直接放弃了数组重新使用了循环链表(当然对数组进行优化也是可以的,但是 我懒 上机时为了节约时间,没有去优化 )

解法二:

链表的话直接使用循环链表,删除查找过的元素,删除操作的时间复杂度是O(1),整体时间复杂度为O(n*m)。

具体代码实现如下:
#include<stdio.h>
#include<malloc.h>
struct node
{
    int item;
    struct node *next;
};
int main()
{
    int n,m,i;
    scanf("%d%d",&n,&m);
    struct node *p;
    p=(struct node*)malloc(sizeof(struct node));
    struct node *head;
    head=(struct node*)malloc(sizeof(struct node));
    head=p;
    p->item=1;
    p->next=p;
    for(i=2;i<=n;i++)
    {
        struct node *p0;
        p0=(struct node*)malloc(sizeof(struct node));
        p0->item=i;
        p->next=p0;
        p=p0;
    }
    p->next=head;
    struct node *p0;
    p0=(struct node*)malloc(sizeof(struct node));
    p0=head;
    for(i=0;i<n;i++)
    {
        if(p0->next==p0)
            printf("%d",p0->item);
        else
        {
            struct node *p3;
            p3=(struct node*)malloc(sizeof(struct node));
            for(int j=1;j<m;j++)
            {
                p3=p0;
                p0=p0->next;
            }
            printf("%d ",p0->item);
            p0=p0->next;
            p3->next=p3->next->next;
        }
    }
}

三.7-3 算术表达式计算 (100 分)

任务: 计算算术表达式的值。

算术表达式按中缀给出,以=号结束,包括+,-,/四种运算和(、)分隔符。运算数的范围是非负整数,没有正负符号,小于等于10^9 。

计算过程中,如果出现除数为0的情况,表达式的结果为”NaN” ; 如果中间结果超出32位有符号整型范围,仍按整型计算,不必特殊处理。 输入保证表达式正确。

  作者               谷方明
  单位               吉林大学
  代码长度限制        16 KB
  时间限制            1000 ms
  内存限制            64 MB

输入格式:

一行,包括1个算术表达式。算术表达式的长度小于等于1000。

输出格式:

一行,算术表达式的值 。

输入样例:

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

(1+30)/3=

输出样例:

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

10

题目解析:

计算中缀表达式的值
/*需要注意题目要求32位整型 所以用 int,千万不要和我一样自作聪明用long long int 不然,,,你会被扣分。 */

解法一:

在读取中缀表达式进行边读取,边计算的操作。
这里我们需要用到两个栈辅助,一个数字栈,一个符号栈。
当接收到一个数字,直接将其压入数字栈,这里由于我们是用字符串形式输入的,所以应该考虑到两位及以上位数的情况。这里我通过判断上一个字符是否为数字来实现。

if(a[i]>='0'&&a[i]<='9'&&a[i-1]>='0'&&a[i-1]<='9'&&i)
        {
            sum[top_1-1]=sum[top_1-1]*10+a[i]-'0';
        }
        else if(a[i]>='0'&&a[i]<='9')
        {
            sum[top_1++]=a[i]-'0';
        }

当接收到运算符,如果遇到了左括号,直接入栈;如果遇到了右括号,那么运算符依次出栈,计算直到左括号出栈。如果栈空或者左侧为左括号,运算符(非括号)直接入栈,否则首先将其与符号栈顶的运算符优先级比较,如果栈顶的优先级大于等于当前运算符,那么栈顶运算符出栈并计算。重复该过程,若字符串读取完毕后符号栈不为空,符号栈元素依次出栈,计算,最后数字栈栈顶为最终结果。
如果出现0作为除数,程序直接退出

if(b[top_2-1]=='/')
{
   if(sum[top_1-1]==0)
   {
       printf("NaN");
       return 0;
   }
}                  

另外,对于优先级的判断,由于想到只有4种运算符,做具体判断也不复杂,就没有编写优先级判断的函数,导致代码有一些冗长,是否写判断函数,根据个人喜好,但个人建议还是写了好亿点。

具体代码实现如下:
#include<stdio.h>
#include<string.h>
int main()
{
    char a[1005];
    scanf("%s",&a);
    int l,i;
    l=strlen(a);
    int sum[500];
    int top_1=0,top_2=0;
    char b[500];
    for(i=0;i<l;i++)
    {
        if(a[i]>='0'&&a[i]<='9'&&a[i-1]>='0'&&a[i-1]<='9'&&i)
        {
            sum[top_1-1]=sum[top_1-1]*10+a[i]-'0';
        }
        else if(a[i]>='0'&&a[i]<='9')
        {
            sum[top_1++]=a[i]-'0';
        }
        else if(a[i]=='(')
        {
            b[top_2++]='(';
        }
        else if(a[i]==')')
        {
            while(b[top_2-1]!='(')
            {
                if(b[top_2-1]=='+')
                {
                    sum[top_1-2]=sum[top_1-2]+sum[top_1-1];
                    top_1--;
                }
                else if(b[top_2-1]=='-')
                {
                    sum[top_1-2]=sum[top_1-2]-sum[top_1-1];
                    top_1--;
                }
                else if(b[top_2-1]=='*')
                {
                    sum[top_1-2]=sum[top_1-2]*sum[top_1-1];
                    top_1--;
                }
                else if(b[top_2-1]=='/')
                {
                    if(sum[top_1-1]==0)
                    {
                        printf("NaN");
                        return 0;
                    }
                    sum[top_1-2]=sum[top_1-2]/sum[top_1-1];
                    top_1--;
                }
                top_2--;
            }
            top_2--;
        }
        else if(a[i]=='+')
        {
            if(b[top_2-1]=='*')
            {
                sum[top_1-2]=sum[top_1-2]*sum[top_1-1];
                top_1--;
                top_2--;
            }
            if(b[top_2-1]=='/')
            {
                if(sum[top_1-1]==0)
                    {
                        printf("NaN");
                        return 0;
                    }
                sum[top_1-2]=sum[top_1-2]/sum[top_1-1];
                top_1--;
                top_2--;
            }
            if(b[top_2-1]=='+')
            {
                sum[top_1-2]=sum[top_1-2]+sum[top_1-1];
                top_1--;
                top_2--;
            }
            if(b[top_2-1]=='-')
            {
                sum[top_1-2]=sum[top_1-2]-sum[top_1-1];
                top_1--;
                top_2--;
            }
            b[top_2++]='+';
        }
        else if(a[i]=='-')
        {
            if(b[top_2-1]=='*')
            {
                sum[top_1-2]=sum[top_1-2]*sum[top_1-1];
                top_1--;
                top_2--;
            }
            if(b[top_2-1]=='/')
            {
                if(sum[top_1-1]==0)
                    {
                        printf("NaN");
                        return 0;
                    }
                sum[top_1-2]=sum[top_1-2]/sum[top_1-1];
                top_1--;
                top_2--;
            }
            if(b[top_2-1]=='+')
            {
                sum[top_1-2]=sum[top_1-2]+sum[top_1-1];
                top_1--;
                top_2--;
            }
            if(b[top_2-1]=='-')
            {
                sum[top_1-2]=sum[top_1-2]-sum[top_1-1];
                top_1--;
                top_2--;
            }
            b[top_2++]='-';
        }
        else if(a[i]=='*')
        {
            if(b[top_2-1]=='*')
            {
                sum[top_1-2]=sum[top_1-2]*sum[top_1-1];
                top_1--;
                top_2--;
            }
            if(b[top_2-1]=='/')
            {
                if(sum[top_1-1]==0)
                    {
                        printf("NaN");
                        return 0;
                    }
                sum[top_1-2]=sum[top_1-2]/sum[top_1-1];
                top_1--;
                top_2--;
            }
            b[top_2++]='*';
        }
        else if(a[i]=='/')
        {
            if(b[top_2-1]=='*')
            {
                sum[top_1-2]=sum[top_1-2]*sum[top_1-1];
                top_1--;
                top_2--;
            }
            if(b[top_2-1]=='/')
            {
                if(sum[top_1-1]==0)
                    {
                        printf("NaN");
                        return 0;
                    }
                sum[top_1-2]=sum[top_1-2]/sum[top_1-1];
                top_1--;
                top_2--;
            }
            b[top_2++]='/';
        }
    }
    for(;top_2>0;top_2--)
    {
        if(b[top_2-1]=='+')
        {
            sum[top_1-2]=sum[top_1-2]+sum[top_1-1];
            top_1--;
        }
        else if(b[top_2-1]=='-')
        {
            sum[top_1-2]=sum[top_1-2]-sum[top_1-1];
            top_1--;
        }
        else if(b[top_2-1]=='*')
        {
            sum[top_1-2]=sum[top_1-2]*sum[top_1-1];
            top_1--;
        }
        else if(b[top_2-1]=='/')
        {
            if(sum[top_1-1]==0)
                    {
                        printf("NaN");
                        return 0;
                    }
            sum[top_1-2]=sum[top_1-2]/sum[top_1-1];
            top_1--;
        }
    }
    printf("%d",sum[0]);
}
解法二:

因为后缀表达式很符合计算机的运算方式,所以将中缀表达式转化为后缀表达式再进行直接计算也是一种不错的方法,具体的转化方式我就不唠叨了,重要的是理解优先级的关系,以及处理好括号。

四.7-4 最喜爱的序列 (100 分)

小唐这段时间在研究序列。拿来N个整数的序列,他给序列中的每个整数都赋予一个喜爱值。喜爱值也是整数,有正有负,越大表明越喜欢。他想知道,如何从序列中连续取最多m个数,他获得喜爱值最大。1≤N≤500000,1≤m≤N。

  作者               谷方明
  单位               吉林大学
  代码长度限制        16 KB
  时间限制            1000 ms
  内存限制            64 MB

输入格式:

第一行是两个整数N,m。分别代表序列中数的个数以及能取的最多个数。

第二行用空格隔开的N个整数,第i个整数Li代表他对第i个数的喜爱值。│Li│≤1000

输出格式:

一行,三个数,表示获得最大喜爱值,及第一个取最大喜爱值的区间。

输入样例:

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

5 2
1 4 5 2 3

输出样例:

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

9 2 3

题目解析:

最长区间为m的连续子序列和的最大值

解法:

当时上机刚开始做的时候没有注意题目的要求,取 最多m个数,以为是取m个数,但当时也采用了队列,用p作为正负值,p为当前所选区间后t项的和 与 当前所选区间内前t项的和 的差值。如果p为正数,就通过 love+=p更新love的值,并改变当前区间前后标识(如果不是 “最多”的限制,也是比较简便的方法/doge,时间复杂度为O(n))

以下是上机时80分的代码:

#include<stdio.h>
int main()
{
    int a[500004];
    int n,m,top,rear;
    long long int love=0,p=0;//p为正负值
    scanf("%d%d",&n,&m);
    top=1;rear=m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(i<=m)love=love+a[i];
        else
        {
            p=p+a[i]-a[i-m];
            if(p>0)
            {
                rear=i;
                top=i-m+1;
                love=love+p;
                p=0;
            }
        }
    }
    while(a[top]<0)
    {
        love=love-a[top];
        top++;
    }
    printf("%lld %d %d",love,top,rear);
}

这个题如果使用优先队列就可以解决最多为m项的问题。用int 型数组sum[i]储存喜爱值的前 i 项和,维护一个区间长度为m,储存区间最小值的单调队列。
在接收到每一个sum[i]时,先对优先队列进行维护,再用sum[i]减去单调队列队首的值得到一个 love 值,再进行判断和更新。
因为对于每一个sum[i],我们都通过单调队列保证了他所减去的 队首值 是他所在区间(也就是他能够减去)的最小值,也就是说,我们计算出了以 1-n 每一个 i 作为区间右边界的最大love值。因此,该算法是合理的。
而对于单调队列中m区间的维护,我是通过建立二维数组实现单点队列,用第二个值记录该值的下标并进行判断。

具体代码实现如下:
#include<stdio.h>
int main()
{
   int M,n,i;
   scanf("%d%d",&M,&n);
   int sum[M+1][2];
   for(i=1;i<=M;i++)
   {
       scanf("%d",&sum[i][0]);
       if(i!=1)
        sum[i][0]=sum[i][0]+sum[i-1][0];
       sum[i][1]=i;
   }
 //  for(i=1;i<=M;i++)printf("\n!  %d  %d",sum[i][0],sum[i][1]);


   int a[M+1][2];
   int left,right,love=-10000;
   int top=0,rear=0;
   a[top][0]=sum[1][0];
   a[top][1]=1;
   rear++;
//  printf("1");
   for(i=2;i<=M;i++)
   {
        if(sum[i][1]-a[top][1]>n)
        top++;
       while(sum[i][0]<a[top][0]&&top!=rear)
       {
           top++;
       }
       a[rear][0]=sum[i][0];
        a[rear][1]=sum[i][1];
        rear++;
       if(sum[i][0]-a[top][0]>love)
       {
           love=sum[i][0]-a[top][0];
           left=a[top][1];
           right=sum[i][1];
       }
   }
 //  printf("asadsa");
   printf("%d %d %d",love,left+1,right);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值