JLU第一次数据结构实验解题报告
目录
一、7-1 重复计数
在一个有限的正整数列中,有些数会多次重复出现。请你统计每个数的出现次数,然后按数字在序列中第一次出现的位置顺序输出数及其次数。
输入格式:
第一行:一个整数N,表示正数的个数(1≤N≤50000)。
第二行:N个正整数,每个整数x都满足1≤2000000000。
输出格式:
若干行,每行两个用一个空格隔开的数,第一个是数列中出现的数,第二个是该数在序列中出现的次数。
输入样例:
12
8 2 8 2 2 11 1 1 8 1 13 13
输出样例:
8 3
2 3
11 1
1 3
13 2
解题思路:
我用的是暴力解法O(n^2),就是先开一个符合题意的数组ent,把第一个数字存入数组第一个元素中,出现次数计为1,然后再读入新的数字,遍历数组ent,新读入的数字出现在ent中,就在ent数组中相应的数字所对应的次数加1,否则,将该数字加入ent数组,次数赋值1。但是它一开始只得了70分,错误是运行超时,我以为是暴力循环太多导致超时,后来听了同学的暴力解法 两个平台都通过了,我重新找了原因。改到最后发现把long long改成 int就通过了,然后突然觉得自己有一个重大发现。
以下是我的代码:
#include<stdio.h>
struct tongji
{
int name;
int cishu;
};
struct tongji ent[50001];
int count=0;
int main()
{
int N[50001];
int i,j;
int n;
scanf("%d",&n);
scanf("%d",&N[0]);
ent[0].name=N[0];
ent[0].cishu=1;
for(i=1;i<n;i++)
{
scanf("%d",&N[i]);
for(j=0;j<=count;j++)
{
if(ent[j].name==N[i])
{
ent[j].cishu++;
break;
}
}
if(j==count+1)
{
count++;
ent[count].name=N[i];
ent[count].cishu=1;
}
}
for(i=0;i<=count;i++)
{
printf("%d %d",ent[i].name,ent[i].cishu);
if(i!=count)printf("\n");
}
return 0;
}
二、7-2报数游戏
n个人围成一圈,从1开始依次编号,做报数游戏。现指定从第1个人开始报数,报数到第m个人时,该人出圈,然后从其下一个人重新开始报数,仍是报数到第m个人出圈,如此重复下去,直到所有人都出圈。总人数不足m时将循环报数。请输出所有人出圈的顺序。
输入格式:
一行,两个整数n和m。n表示游戏的人数,m表示报数出圈的数字,1≤n≤50000,1≤m≤100。
输出格式:
一行,n个用空格分隔的整数,表示所有人出圈的顺序
- 输入样例:
5 2
输出样例:
2 4 1 5 3
分析思路:这是约瑟夫问题,之前遇到过两次,我都是用数组做的,不过循环链表也可。数组是也是模拟的循环链表,他相比于链表来说的缺点是空间会被浪费掉。听同学的方法是给那个该出圈的人一个特别的标记就可以,下次访问到跳过就OK,这个方法也很好。
这道题刚开始60分,原因也是我误用了 long long!!改成int 就好了。
以下是我的代码:
#include<stdio.h>
int main()
{
int a[100];
int i,m,n,k,j=0;
scanf("%d %d",&n,&m);
for(i=0;i<n;i++)
{
a[i]=i+1;
}
while(n>1)
{
j=(j+m-1)%n; //被选中出圈人的序号
printf("%d ",a[j]); //把他删除
for(k=j;k<n-1;k++)
{
a[k]=a[k+1]; //出圈人后面的前移,保证圈不断开
}
n--; // //把出圈的人删除
}
printf("%d",a[0]);
return 0;
}
三、7-3 算数表达式计算
任务:计算算术表达式的值。
算数表达式按中缀形式给出,以“=”结束,包括+、-、*、/四种运算和(、)分隔符。运算数的范围是非负整数,没有正负号,小于等于109.
计算过程中,如果出现除数为0的情况,表达式结果为“NAN”;如果中间结果超出32位有符号整形范围,仍按整型计算,不必特殊处理。输入保证表达式正确。
输入格式:
一串以=结束的合法算术表达式
输出格式:
算术表达式结果
输入样例:
(30+1)/3=
输出样例:
10
解题思路:
我的思路就是用的课上老师教的计算中缀表达式算法思想。辅助空间用两个栈,一个存数字,一个存运算符。我在实现代码的过程中犯了两个错误,一个是没有随时判断栈空导致段错误,一个是没有比较+、-、*、/自身与自身的优先级比较,导致出现了段错误与答案错误。具体实现请看代码。
#include<bits/stdc++.h>
using namespace std;
stack <char> character;
stack <int> num;
bool Judge(char x){
if(x>='0'&&x<='9')
return true;
return false;
}
char compare(char x,char y) //比较输入运算符和栈顶字符的优先级
{
if(x=='+'){
if(y=='*'||y=='/'||y=='-'||y=='+'){return '<';} //优先级相等的也弹出去参与计算
else return '>';
}
if(x=='-'){
if(y=='*'||y=='/'||y=='+'||y=='-'){return '<';}
else return '>';
}
if(x=='*'){
if(y=='/'||y=='*')return '<';
else return '>';
}
if(x=='/'){
if(y=='*'||y=='/')return '<';
else return '>';
}
if(x=='('){
return '>';
}
}
void comput(char ent)
{
int temp2=num.top();
num.pop();
int temp1=num.top();
num.pop();
switch(ent){
case'+':{num.push(temp1+temp2);break;}
case'-':{num.push(temp1-temp2);break;}
case'*':{num.push(temp1*temp2);break;}
case'/':{
if(temp2==0){
printf("NaN");
exit(0);
}
else{
// printf("%d ",temp2);
num.push(temp1/temp2);
}
break;
}
}
// printf("%d%c%d=%d ",temp1,ent,temp2,num.top());
}
int main()
{
char ch,com;
int flag,j;
ch=getchar();
while(ch!='='){
j=0;
flag=0;
while(Judge(ch)){
j=j*10+ch-'0';
ch=getchar();
flag=1;
}
if(flag==1){num.push(j);}
else{
if(character.empty()) {
character.push(ch);
}
else{
if(ch==')'){
while(character.top()!='('){
comput(character.top());
character.pop();
}
character.pop();
}
else{
com=compare(ch,character.top());
while((com=='<'&&!character.empty())){
comput(character.top());
character.pop(); //有弹出就得判空,防止段错误,我在这儿出过错
if(character.empty()){break;}
com=compare(ch,character.top());
}
character.push(ch);
}
}
ch=getchar();
}
}
if(ch=='='){
while(!character.empty()){
comput(character.top());//有弹出就得判空
character.pop();
}
}
cout<<num.top();
return 0;
}
四、 7-4最喜爱的序列
小唐这段时间在研究序列。拿来N个整数的序列,他给序列中的每个整数都赋予一个喜爱值。喜爱值也是整数,有正有负,越大表明越喜欢。他想知道,如何从序列中连续取最多m个数,他获得喜爱值最大。1≤N≤500000,1≤m≤N。
输入格式:
第一行是两个整数N,m。分别代表序列中数的个数以及能取的最多个数。第二行用空格隔开的N个整数,第i个整数Li代表他对第i个数的喜爱值。│Li│≤1000
输出格式:
一行,三个数,表示获得最大喜爱值,及第一个取最大喜爱值的区间。
输入样例:
5 2
1 4 5 2 3
输出样例:
9 2 3
解题思路:
刚开始确实理解错了题意,连续最多m个数,最多,不是固定长度m。当时看这个题目,觉得也不难嘛,不需要用双端队列。理解错题意竟然得了80分,后来问了一位大佬,才知道自己读错题目了。继续用数组就运行超时了。不得不向大佬学习巩固双端队列了。先求前缀和,存到一个数组,然后让数组元素下标进入队列,维护下标做对应元素递减次序并且队头队尾所表示区间长度不超过m。循环判断队头队尾元素之差与所保存的maxn的值的大小,如果大于,更新maxn,并保存对应区间。
以下是实现的代码:
#include<bits/stdc++.h>
using namespace std;
deque<int> q;
int main(void)
{
int n, m, i, j;
int front,rear;
long long manx=1e-5;
scanf("%d %d",&n,&m);
long long line[500001];
line[0]=0;
for (i = 1; i <= n; i++)
{
scanf("%lld", &line[i]);
line[i] += line[i-1];
}
long long maxn = 0;
while (!q.empty())
q.pop_back(); //弹出最后一个并把容器减小一个
q.push_front(0); //把下标0压入
for (i=1; i<=n;i++)
{
while (!q.empty() && line[q.front()] > line[i]) //保持递减序列
{
q.pop_front();
}
q.push_front(i);
while (!q.empty() && i - q.back() > m)
{
q.pop_back();//如果区间的大了,就弹出dui头
}
if(maxn< line[i] - line[q.back()])
{
maxn = line[i] - line[q.back()];
front = q.back()+1;
rear = i;
}
}
printf("%lld %d %d\n", maxn, front,rear);
return 0;
}
交流解题报告真的是一种不错的体验,原来我在优快云上看到的写文章的大佬就在我身边 ,向你们看齐,加油!