1. 程式人生 > 其它 >資料結構榮譽課---第一次實驗解題報告

資料結構榮譽課---第一次實驗解題報告

資料結構榮譽課---第一次實驗解題報告

一,重複計數

題目

在一個有限的正整數序列中,有些數會多次重複出現。請你統計每個數的出現次數,然後按數字在序列中第一次出現的位置順序輸出數及其次數。
輸出格式:
若干行,每行兩個用一個空格隔開的數,第一個是數列中出現的數,第二個是該數在序列中出現的次數。

輸入樣例:
在這裡給出一組輸入。例如:
在這裡插入圖片描述

輸出樣例:
在這裡給出相應的輸出。例如:
在這裡插入圖片描述

解題思路

用了O(n^2)的方法,在兩個平臺上都滿分通過了,所以只寫了這個方法的解題思路。

  • 開闢兩個長度為n的陣列,一個記錄讀入的資料,另一個所有元素初始化為0,記錄讀入的資料是不是第一次出現,如果不是第一次出現,將相應的0改為1。
  • 初始化一個變數count為1,數字重複出現,則count++,迴圈結束時將count打印出來,再次初始化為1,為下一個資料的統計頻率做準備。

程式碼

#include<stdio.h>
#include<stdlib.h>
int main() {
	int N;
	int a[50001], b[50001] = { 0 };
	int count=1, i, j;
	scanf("%d", &N);
	for (i =
0; i < N; i++) { scanf("%d", &a[i]); } for (i = 0; i < N; i++) { if (b[i]==0) { count = 1;//初始化 for (j = i + 1; j < N; j++) { if (a[i] == a[j]) { b[j] = 1;//標記出現過的資料 count++;//統計頻率 } } printf("%d %d\n", a[i], count); } } }

二,報數遊戲

題目

n個人圍成一圈,從1開始依次編號,做報數遊戲。 現指定從第1個人開始報數,報數到第m個人時,該人出圈,然後從其下一個人重新開始報數,仍是報數到第m個人出圈,如此重複下去,直到所有人都出圈。總人數不足m時將迴圈報數。請輸出所有人出圈的順序。

輸入格式:
一行,兩個整數n和m。n表示遊戲的人數,m表示報數出圈的數字,1≤n≤50000,1≤m≤100.

輸出格式:
一行,n個用空格分隔的整數,表示所有人出圈的順序
輸入樣例:
在這裡給出一組輸入。例如:
在這裡插入圖片描述

輸出樣例:
在這裡插入圖片描述

解題思路

  • 這個題是約瑟夫環問題,比較簡單。
  • 注意當數到第n個人時將i重置為0.
  • 老師說採用連結串列結構會更高效,但我還沒有試。
 if (i == n) {
            i = 0;  //從頭開始數
        }

程式碼

#include <stdio.h>

void left(int* a,int n,int m) {
    int out = 0,count = 0,i = 0;    
    int *p = a;
    int num = 0;
    for(num = 0;num < n;num++) {
        *(a+num) = num+1;//初始化
    } 
    while (out < n-1) {
        if (*(p+i) != 0) {
            count ++;  
        }
        if (count == m) {//數到了第m個人
            count = 0;//將count初始化,重新開始數
            printf("%d ",*(p+i));
            *(p+i) = 0;//將數到的人賦值為0,不計入迴圈
            out++;  
        }
        i++;
        if (i == n) {
            i = 0;  //從頭開始數
        }
    }
    for (num = 0; num < n; num++) {
        if (*(a+num) != 0) {
            printf("%d\n",*(a+num));//最後剩下的人
        }
    }
}

int main()
{
    long n;
    int m;
    long a[50000] = {0};
    scanf("%d",&n);
    scanf("%d",&m);   
    left(a,n,m);
    return 0;
}

三,算術表示式

題目

任務: 計算算術表示式的值。

算術表示式按中綴給出,以=號結束,包括+,-,/四種運算和(、)分隔符。運算數的範圍是非負整數,沒有正負符號,小於等於109 。

計算過程中,如果出現除數為0的情況,表示式的結果為”NaN” ; 如果中間結果超出32位有符號整型範圍,仍按整型計算,不必特殊處理。 輸入保證表示式正確。

輸入格式:
一行,包括1個算術表示式。算術表示式的長度小於等於1000。

輸出格式:
一行,算術表示式的值 。

輸入樣例:
在這裡給出一組輸入。例如:
在這裡插入圖片描述
輸出樣例:
在這裡給出相應的輸出。例如:
在這裡插入圖片描述

解題思路

  • 優先順序的比較:建一個函式,如果是‘(’或’)'相應的x標記為0,如果是‘+’,‘-’相應的x標記為1,如果是‘*’,‘/’相應的x標記為2。
int first(char ch) {
	int x = 0;
	switch (ch) {
	case '(':
		x = 0; break;
	case ')':
		x = 0; break;
	case '+':
		x = 1; break;
	case '-':
		x = 1; break;
	case '*':
		x = 2; break;
	case '/':
		x = 2; break;
	}
	return x;
}
  • 建兩個棧,一個存運算子,一個存數字。如果運算子棧的棧頂元素的優先順序低於讀入運算子的優先順序,則將讀入運算子進行壓棧操作,反之,彈出棧頂元素,呼叫運算函式,運算結果,然後將運算結果壓入數字棧。
  • 在數字棧記憶體入資料時,需進行將char型資料轉換成int型資料的操作。這裡開闢了一個數組,如果數字是兩位以上的數字,則分開儲存。最後用atoll函式將string型別的資料轉換為int型別。
  • 還需注意的一個點是題目要求如果中間結果超出32位有符號整型範圍,仍按整型計算,不必特殊處理。但我在課上寫的時候沒有注意看,使用了long long型棧,導致有一個10分的測試點一直過不去。
  • 此程式碼還有一個問題是如果我只輸入一個數字它會輸出NaN,試了挺久我也沒發現是怎麼回事,不影響得分我就沒再糾結了,希望有空的大佬能幫我解答一下。

程式碼

#include<stdio.h>
#include<iostream>
#include<stack>
#include<string>
#include<stdlib.h>
#define maxsize 1001
using namespace std;
stack<char> s1;
stack<int> s2;
int first(char ch);
int yunsuan(int a, int b, char ch);
void result(char str[]);
int first(char ch) {
	int x = 0;
	switch (ch) {
	case '(':
		x = 0; break;
	case ')':
		x = 0; break;
	case '+':
		x = 1; break;
	case '-':
		x = 1; break;
	case '*':
		x = 2; break;
	case '/':
		x = 2; break;
	}
	return x;
}
int yunsuan(int a, int b, char ch)
{
	int c = 0;
	switch (ch)
	{
	case '+':
		c = a + b;
		break;
	case '-':
		c = a - b;
		break;
	case '*':
		c = a * b;
		break;
	case '/':
		if (b == 0)
		{
			cout << "NaN" << endl;
			exit(0);
		}
		else
			c = a / b;
		break;
	}
	return c;
}
void result(char str[])
{
	char num[maxsize];
	char top1;
	int f, c, get1, get2;
	int j, i;
	for (i = 0; str[i] != '='; i++)
	{
		switch (str[i])
		{
		case '(':
			s1.push(str[i]);
			break;
		case '+':
		case '-':
		case '*':
		case '/':
			if ((s1.empty()))
				s1.push(str[i]);
			else if (first(str[i]) > first(s1.top()))
				s1.push(str[i]);
			else {
				while (!s1.empty() && first(str[i]) <= first(s1.top()) && s1.top() != '(')
				{
					get1 = s2.top();
					s2.pop();
					get2 = s2.top();
					s2.pop();
					top1 = s1.top();
					c = yunsuan(get2, get1, top1);
					s1.pop();
					s2.push(c);
				}
				s1.push(str[i]);
			}
			break;
		case ')':
			while (s1.top() != '(')
			{
				get1 = s2.top();
				s2.pop();
				get2 = s2.top();
				s2.pop();
				top1 = s1.top();
				s1.pop();
				c = yunsuan(get2, get1, top1);
				s2.push(c);
			}
			s1.pop();
			break;
		default:
			j = 0;
			do
			{
				num[j++] = str[i];
				i++;
			} while (str[i] >= '0' && str[i] <= '9');
			num[j] = '\0';
			f = atoll(num);
			i--;
			s2.push(f);
			break;
		}
	}
	while (!s1.empty())
	{
		get1 = s2.top();
		s2.pop();
		get2 = s2.top();
		s2.pop();
		top1 = s1.top();
		s1.pop();
		c = yunsuan(get2, get1, top1);
		s2.push(c);
	}
	printf("%d", s2.top());
}
int main() {
	char str[maxsize];
	scanf("%s", str);
	result(str);
	return 0;
}

四,最喜愛的序列

題目

小唐這段時間在研究序列。拿來N個整數的序列,他給序列中的每個整數都賦予一個喜愛值。喜愛值也是整數,有正有負,越大表明越喜歡。他想知道,如何從序列中連續取最多m個數,他獲得喜愛值最大。1≤N≤500000,1≤m≤N。
輸入格式:
第一行是兩個整數N,m。分別代表序列中數的個數以及能取的最多個數。

第二行用空格隔開的N個整數,第i個整數Li代表他對第i個數的喜愛值。│Li│≤1000

輸出格式:
一行,三個數,表示獲得最大喜愛值,及第一個取最大喜愛值的區間。

輸入樣例:
在這裡給出一組輸入。例如:
在這裡插入圖片描述
輸出樣例:
在這裡給出相應的輸出。例如:
在這裡插入圖片描述

解題思路

  • 開闢一個數組,讀入資料的同時,記錄前n項的和,以便後續計算。
for (int i = 1; i <= N; i++) {
		cin >> array[i];
		array[i] += array[i - 1];//記錄前n項的和
	}
  • 使用STL中的deque雙端佇列,每讀取一個元素就和隊尾元素進行比較,如果隊非空,且當前元素小於隊尾元素,就彈出隊尾元素,重複以上操作,直到隊空或者隊尾元素小於當前元素,如果隊首元素的編號不在區間內,則彈出隊首元素,重複以上操作。
  • 初始化一個變數儲存當前區間的元素之和,然後和最大元素之和比較,如果當前區間的元素之和大於最大元素之和,則更新最大元素之和。

程式碼

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<deque>
using namespace std;
deque<int> x;
int main() {
	int N, m, front, rear;
	cin >> N;
	cin >> m;
	int array[500001];
	array[0] = 0;
	for (int i = 1; i <= N; i++) {
		cin >> array[i];
		array[i] += array[i - 1];//記錄前n項的和
	}
	int sum = 0;
	while (!x.empty())
		x.pop_back();
		x.push_front(0);
	for (int i = 1; i <= N; i++) {
		while (!x.empty() && array[x.front()] > array[i]) {//判斷佇列是否為空,彈出大於當前元素的節點的編號
			x.pop_front();
		}
		x.push_front(i);//當前元素的編號入隊
		while (!x.empty() && i - x.back() > m) {
			x.pop_back();
		}
		int tmp=array[i] - array[x.back()];
		if (sum < tmp) {//判斷是否更新最大值
			sum = tmp;
			front = x.back() + 1;
			rear = i;
		}
	}
	cout<<sum<<" "<<front<<" "<<rear;
	return 0;
}