PTA 乙級難點(全部)

PAT (Basic Level) Practice (中文)點擊跳轉到PAT


1002 寫出這個數(字符串)

在這裏插入圖片描述

#include<iostream>
using namespace std;
char op[1000];
char ed[10][5]={"ling","yi","er","san","si","wu","liu","qi","ba","jiu"};
int boss[100];
int pep=0,j=0;
int main()
{
    cin>>op; //char數組可以直接輸入字符串
    for(int i=0;op[i]!='\0';i++)
    {
        pep+=op[i]-'0';
    }
    while(pep!=0)
    {
        boss[j]=pep%10;
        pep/=10;
        j++;
    }
    j--;
    for(int i=j;i>=0;i--)
    {
        cout<<ed[boss[i]];
        if(i!=0) cout<<' ';
    }
    return 0;
}

1003 我要通過!

在這裏插入圖片描述
在這裏插入圖片描述

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
    int cnt;
    scanf("%d",&cnt);
    while(cnt--)
    {
        char str[1000];
        cin>>str;
        int i,state=0,Afront=0,Amiddle=0,Alater=0;
        for(i=0;i<strlen(str);i++)
        {
            if(str[i]!='P'&&str[i]!='A'&&str[i]!='T') 
                break;
            if(str[i]=='P')
                if(state==0)
                    state=1;
                else
                    break;
            if(str[i]=='A')
                if(state==1)
                    state=2;
                else if(state==2)
                    Amiddle++;
                else if(state==0)
                    Afront++;
                else
                    Alater++;
            if(str[i]=='T')
                if(state==2)
                    state=3;
                else
                    break;
        }
        while(Amiddle--)
        {
            Alater-=Afront;
        }
        if(state!=3||Afront!=Alater||i<strlen(str))
            cout<<"NO"<<endl;
        else
            cout<<"YES"<<endl;
    }
    return 0;
}

分析題目三個通過條件可知:字符串中不能出現P、A、T之外的字符;PT之間只有一個A時(PAT),前後可以加上相同個數的A;PT中每多一個A(>2),後面加上前面個數個A,

統計字符串中P、T前中後A的個數判斷即可


1004 成績排名

在這裏插入圖片描述

#include<iostream>
using namespace std;
typedef struct Student
{
    char name[100];
    char number[100];
    int score;
}student;
student _max={"","",0},_min={"","",100};
int main()
{
    int cnt;
    cin>>cnt;
    while(cnt--)
    {
        student use;
        cin>>use.name;
        cin>>use.number;
        cin>>use.score;
        if(use.score>_max.score)
            _max=use;
        if(use.score<_min.score)
            _min=use;
    }
    cout<<_max.name<<" "<<_max.number<<endl;
    cout<<_min.name<<" "<<_min.number;
    return 0;
}

注意 typedef 的用法,還有 max min new 不可以作爲變量名使用,再提醒一下:結構體之間是可以賦值的


1005 繼續(3n+1)猜想

在這裏插入圖片描述

#include<iostream>
using namespace std;
int process[4500]; //過程數
int cnt[101]; //當前數
void progress(int x)
{
    if(process[x]) //避免重複記錄
        return;
    while(x!=1)
    {
        if(x%2==0)
            x/=2;
        else
            x=(x*3+1)/2;
        process[x]=1;
    }
}
int main()
{
    int num,tmp;
    cin>>num;
    while(num--)
    {
        cin>>tmp;
        cnt[tmp]=1;
        progress(tmp);
    }
    int firstblood=0; //題目要求最後一個數字後沒有空格
    for(int i=100;i>0;i--)
    {
        if(cnt[i]&&process[i]==0&&firstblood==0)
        {
            firstblood=1;
            cout<<i;
        }
        else if(cnt[i]&&process[i]==0&&firstblood==1)
            cout<<" "<<i;
    }
    return 0;
}

題目意思是第一個直接執行3n+1猜想,並記錄過程中的每個數,注意是過程數;後面的每個數先判斷是否已經記錄過,記錄過就直接跳過(記錄過後面算就沒意義了),沒記錄過就執行3n+1猜想並記錄過程數


1008 數組元素循環右移問題

在這裏插入圖片描述

#include<iostream>
using namespace std;
int array[105];
int main()
{
	int num,run;
	cin>>num>>run;
	for(int i=1;i<=num;i++)
		cin>>array[i];
    int begin=num-run%num+1; //解決數循環移次數的方法
    for(int i=1;i<=num;i++)
    {
        if(begin>num) //再打印 begin 之前的
            begin=1;
        cout<<array[begin++]; //技巧處,先從 begin 開打印
        if(i<num)
            cout<<' ';
    }
	return 0;
}

題目本身不難,但要關注本代碼的小技巧


1009 說反話

在這裏插入圖片描述

#include<iostream>
using namespace std;
char a[81][81],c;
int length=80,i=0;
int main()
{
    while((c=getchar())!='\n') //典型的字符串輸入
    {
        if(c==' ')
        {
            a[length][i]='\0';
            length--; //從後往前
            i=0;
            continue;
        }
        else
        {
            a[length][i]=c;
            i++;
        }
    }
    a[length][i]='\0';
    for(int j=length;j<=80;j++)
    {
        if(j==80)
            cout<<a[j];
        else
            cout<<a[j]<<' ';
    }
    return 0;
}

特別注意:輸出字符串時,程序讀取到 '\0' 後面都不會輸出,如 "123\0456",只打印 123


1010 一元多項式求導

在這裏插入圖片描述

#include<iostream>
using namespace std;
int main()
{
	int a,b,flag=0;
    while(cin>>a>>b) //典型的數字輸入
    {
    	if(a*b)
    	{
    		if(flag)
    			cout<<' ';
    		else
    			flag=1;
    		cout<<a*b<<' '<<b-1;
		}
	}
	if(!flag) //題目原話 : 注意 “零多項式” 的指數和係數都是 0,但是表示爲 0 0
		cout<<0<<' '<<0;
    return 0;
}

可以利用規律來相乘和減 1,比如例題中 3 * 4 = 12 ,- 5 * 2 = -10 ,6 * 1 = 6 ,- 2 * 0 = 0,這是對應的係數,4 - 1 = 3 ,2 - 1 = 1 ,1 - 1 = 0,這是指數,最後一個是常數 -2,指數不做運算,這是要注意的

另外本代碼的 flag 有兩個用處,一是解決題目要求的:結尾不能有多餘空格,二是滿足題目:“零多項式” 的指數和係數都是 0,但是表示爲 0 0


1012 數字分類

在這裏插入圖片描述
在這裏插入圖片描述

#include<iostream>
#include <iomanip> //此頭文件也別忘了
using namespace std;
int main()
{
    int cnt,A=0,B=-1,C=0,E=0,B2=0,B3=0;
    double D=0,D2=0; //記得浮點數要用 double
    cin>>cnt;
    while(cnt--)
    {
        int num;
        cin>>num;
        if(num%5==0&&num%2==0)
            A+=num;
        if(num%5==1)
        {
            B*=-1;
            B2+=num*B;
            B3=1;
        }
        if(num%5==2)
            C++;
        if(num%5==3)
        {
            D+=num;
            D2++;
        }
        if(num%5==4)
        {
            if(num>E)
                E=num;
        }
    }
    if(A==0)
        cout<<"N ";
    else
        cout<<A<<' ';
    if(B3==0)
        cout<<"N ";
    else
        cout<<B2<<' ';
    if(C==0)
        cout<<"N ";
    else
        cout<<C<<' ';
    if(D2==0)
        cout<<"N ";
    else
        cout<<setprecision(1)<<fixed<<D/D2<<' ';
    if(E==0)
        cout<<"N";
    else
        cout<<E;
    return 0;
}

本代碼是每輸入一個數就判斷一個數,會比較方便和省時

補充關於浮點數精度的知識點
在這裏插入圖片描述


1015 德才論

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct student
{
	char id[10];
	int de, cai, sum;
	int flag;
}stu[1000010]; //根據題目要求元素小於等於 10的 5次方
bool cmp(student a,student b) //關鍵代碼,用於分類排序
{
	if (a.flag != b.flag)  return a.flag < b.flag; //按類分 
	else if (a.sum != b.sum) return a.sum>b.sum; //按總分 
	else if (a.de != b.de)  return a.de > b.de; //按德分 
	else return strcmp(a.id, b.id) < 0; //按准考證號分
}
int main()
{
	int n, l, h;
	scanf("%d%d%d", &n, &l, &h);
	int m = n;  //m表示及格人數
	for (int i = 0; i < n; i++)
	{
		scanf("%s %d %d", &stu[i].id, &stu[i].de, &stu[i].cai); 
		stu[i].sum = stu[i].de + stu[i].cai; //先把總分算出來有利於後期處理 
		if (stu[i].de < l || stu[i].cai < l) //先找出不及格者
		{
			stu[i].flag = 5;
			m--;
		}
		else if (stu[i].de >= h&&stu[i].cai >= h)
		{
			stu[i].flag = 1;
		}
		else if (stu[i].de >= h&&stu[i]. cai < h)
		{
			stu[i].flag = 2;
		}
		else if (stu[i].de >= stu[i].cai)
		{
			stu[i].flag = 3;
		}
		else
		{
			stu[i].flag = 4;
		}
	}
	sort(stu, stu + n, cmp);
	printf("%d\n", m);
	for (int i = 0; i < m; i++)
	{
		printf("%s %d %d\n", stu[i].id, stu[i].de, stu[i].cai); //這裏用 printf,不然換做 cout會超時 
	}
	return 0;
}

本代碼總體是:先分類,再排序

題目要求:考生按輸入中說明的規則從高到低排序,當某類考生中有多人總分相同時,按其德分降序排列,若德分也並列,則按准考證號的升序輸出

補充一點:strcmp 的用處如下

基本形式爲 strcmp(str1,str2)
若str1=str2,則返回零
若str1<str2,則返回負數
若str1>str2,則返回正數
(即:兩個字符串自左向右逐個字符相比(按ASCII值大小相比較),直到出現不同的字符或遇'\0'爲止)

故代碼中的 strcmp(a.id, b.id) < 0 代表升序(從小到大),記住這種表達方式~


1017 A除以B

在這裏插入圖片描述

#include <iostream>
using namespace std;
int main()
{
	string s;
	int a, t = 0, temp = 0; //t是商 temp是餘數 
	cin >> s >> a;
	int len = s.length(); //記住長度用這種寫法 
	t = (s[0] - '0') / a; //字符轉換成數字,再求商 
	if ((t != 0 && len > 1) || len == 1) //之所以額外加 len,是因爲後面的 for循環是從 i=1開始 
		cout << t;
	temp = (s[0] - '0') % a;
	for (int i = 1; i < len; i++) //從 i=1開始 
	{
	t = (temp * 10 + s[i] - '0') / a;
	cout << t;
	temp = (temp * 10 + s[i] - '0') % a;
	}
	cout << " " << temp;
	return 0;
}

代碼內容是:模擬手動除法的過程,每次用第一位去除以B,如果得到的商不是0就輸出,否則就 * 10 + 下一位,直到最後的數爲餘數
注意:存在除數爲 0 的情況


1023 組個最小數

在這裏插入圖片描述

#include<iostream>
using namespace std;
int main()
{
	int book[10]={0};
	for(int i=0;i<10;i++)
		cin>>book[i];
	int index=1; //從 1開始,因爲 0不能做開頭
	while(book[index]==0) //沒有 1再往後找開頭
		index++;
	book[index]--;
	cout<<index;
	index=0;
	while(index<=9)
	{
		if(!book[index])
		{
			index++;
			continue;	
		}
		cout<<index;
		book[index]--;
	}
	return 0;
}

也是用到了小技巧:先確定開頭,再往後從小到大添加數字


1024 科學計數法

在這裏插入圖片描述

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
    char str[10010];
    cin>>str;
    int len = strlen(str);
    if(str[0]=='-') cout<<'-';
	int pos=0; //E的下標 
	while(str[pos]!='E') pos++;
	
	int exp=0; //指數 
	for(int i=pos+2;i<len;i++) //這裏是 <號是因爲 len代表長度而不是下標 
		exp=exp*10+(str[i]-'0');
	if(exp==0) //指數爲 0的特例 
		for(int i=1;i<=pos;i++)
		{
			cout<<str[i];
			return 0; //一定要記得 
		}
		
	if(str[pos+1]=='-') //指數爲負數:考慮 0的位置 
	{
		cout<<"0.";
		for(int i=1;i<exp;i++) cout<<'0';
		cout<<str[1]; //省去了不必要的麻煩 
		for(int i=3;i<pos;i++) cout<<str[i];
	}
	else //指數爲正數:考慮 .的位置 
	{
		for(int i=1;i<pos;i++)
		{
			if(str[i]=='.') continue; //直接略過原來的 .號
			cout<<str[i];
			if(exp<pos-3&&i==exp+2) cout<<'.'; //注意條件 
		} 
		for(int i=0;i<exp-(pos-3);i++) cout<<'0'; //注意條件 
	}
    return 0;
}

本題難點就在於條件

代碼順序是:先打出指數是0的特例(省去後面麻煩),再判斷指數爲正與負並寫出各個結果

當指數爲正數時,可以直接略過原來的 .號只考慮如何加新的 . 號,其中 (i==exp+2) && (pos-3 != exp) 可以打個草稿分析(前者+2是加上了符號和 . 號)(後者-3是減去了符號和 . 號和 E 號)

注意題目要求:正數不用輸出+號、並保證所有有效位都被保留,包括末尾的 0


1025 反轉鏈表

在這裏插入圖片描述
在這裏插入圖片描述

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int first, k, n, temp;
	cin >> first >> n >> k; //首地址,節點個數,反轉數
	int *data = new int[100005];
	int *next = new int[100005]; //這兩個用了new,不然運行出現棧溢出
	int list[100005]; //數組list用來按順序存放地址
	//address data next
	for (int i = 0; i < n; i++) //結點地址,該結點保存的整數數據,下一結點的地址 
	{
		cin >> temp;
		cin >> data[temp] >> next[temp];
	}
	int sum = 0; //真正在鏈表中的結點個數
	//建立正序地址數組list
	while (first != -1) //巧妙的用法
	{
		list[sum++] = first;
		first = next[first];
	}
	//反轉
	for (int i = 0; i < sum/k ; i++) //此處的 sum 代表元素個數而不是下標,不然要先 sum--
	{
		reverse(begin(list) + i*k, begin(list) + i*k + k); //見補充
	}
	for (int i = 0; i < sum - 1; i++)
		printf("%05d %d %05d\n", list[i], data[list[i]], list[i + 1]);
	printf("%05d %d -1", list[sum - 1], data[list[sum-1]]);
	delete[] data;
	delete[] next;
	return 0;
}

主要步驟:先對原列表排序,再反轉
注意:應該考慮輸入樣例中有不在鏈表中的結點的情況,所以用個sum計數
補充new和delete的用法回顧 、reverse()函數的第二個參數是數組最後一個元素的下一個地址


1026 程序運行時間

在這裏插入圖片描述

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
	int time1,time2;
	cin>>time1>>time2;
	double time=(time2-time1)/100.0-(time2-time1)/100; //整型除以浮點型得浮點型
	if(time>=0.5)									   //整形減去浮點型也得浮點型
		time=(time2-time1)/100+1;
	else
		time=(time2-time1)/100;
	int sp=time;
	printf("%02d:%02d:%02d\n",sp/3600,sp%3600/60,sp%3600%60);
	return 0;
}

題目本身不難,但注意代碼的技巧性,本題運用了四捨五入的簡易版


1027 打印沙漏

在這裏插入圖片描述

#include<iostream>
#include<cmath> 
using namespace std;
int main()
{
    int n;
    char c;
    cin >> n >> c;
 
    int judge = ( n + 1) / 2;
    int hang = sqrt(judge); //半個的完整的沙漏層數
    int res = n - 2 * hang * hang + 1; //多餘沙漏數 
 
    for(int i = 1; i <= hang ; i ++)
    {
        int num1 = i - 1;
        for(int j = 0 ; j < num1 ; j ++)
            cout << ' ';
        int num2 = 2 * (hang - i + 1 ) - 1;
        for(int j = 0; j < num2 ; j ++)
            cout << c;
        cout << endl;
    }
    for(int i = 2 ; i <= hang ; i++)
    {
        int num1 = hang - i;
        for(int j = 0 ; j < num1 ; j ++)
            cout << ' ';
        int num2 = 2 * i - 1;
        for(int j = 0; j < num2 ; j++)
            cout << c;
        cout << endl;
    }
    cout << res ;
	return 0;
}

分析
明確了 整個沙漏的字符數(2k*k-1)與 半個的完整的沙漏層數(k)與 每一層沙漏個數(2k-1) 的關係即可

整個沙漏的形狀爲: 2k-1 , 2k-3 , … ,5 ,3, 1 , 3, 5, … ,2k-3 , 2k-1 ( k 代表半個的完整的沙漏層數)

整個沙漏的字符數爲 2k*k -1 (用到了等差數列求和)

補充:對於輸入的 n ,其實也只需要判斷(n+1)/2是不是開方數,但是不管它是不是開方數,開方的結果都是 k

等差數列求和回顧


1028 人口普查

在這裏插入圖片描述

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	string date,date_old="2014/09/06",date_young="1814/09/06";
	string name,name_old,name_young;
	int n,count=0;
	cin>>n;
	while(n--)
	{
		cin>>name>>date;
		if(date>="1814/09/06"&&date<="2014/09/06")
		{
			count++;
			if(date<date_old)
			{
				date_old=date;
				name_old=name;
			}
			if(date>date_young)
			{
				date_young=date;
				name_young=name;
			}
		}
	}
	if(count)
	cout<<count<<' '<<name_old<<' '<<name_young;
	else
	cout<<0;
	return 0;
}

注意題目:這裏確保每個輸入的日期都是合法的,但不一定是合理的

分析:利用 string 的特性,使字符串間可以直接比較大小,然後求最值即最年輕的和最年長的,最後注意一個坑點,可能會有0個符合的年份,則只輸出0


1029 舊鍵盤

在這裏插入圖片描述

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; 
int main()
{
	char str1[90], str2[90];
	cin>>str1>>str2;
	int len1 = strlen(str1);
	int len2 = strlen(str2);
	int Bool[128] = {0}; //因爲 ASCII碼錶裏的字符總共有 128個 
	for (int i = 0; i < len1; i++)
	{
		int j = 0; //重新計數 
		char c1;
		char c2;
		for ( j ; j < len2; j++)
		{
			c1 = str1[i];
			c2 = str2[j];
			if (c1 >= 'a'&&c1 <= 'z') c1 -= 32; //保證只在此循環內改變字母大小寫 
			if (c2 >= 'a'&&c2 <= 'z') c2 -= 32;
			if (c1 == c2) break; //在str2找得到str1的字符 
		}
		if (j == len2 && Bool[c1] == 0) //判斷輸出並記錄
		{
			cout<<c1;
			Bool[c1] = 1;
		}
	}
	return 0;
}

題目和代碼本身不難,但要記住這種判斷輸出並記錄 的方法


1030 完美數列

在這裏插入圖片描述

#include <iostream>
#include <algorithm>
using namespace std;
int n, q, a[100010];
int binarySearch(int i, long long x) //二分法 找到第一個大於或者等於a[i]*q的數
{
	if (a[n - 1] <= x)  return n;
	int left = i + 1, right = n - 1, mid; //此處賦值與二分法無關
	while (left < right)
	{
		mid = (left + right) / 2;
		if (a[mid] <= x) left = mid + 1;
		else right = mid;
	}
	return left; //right也可以 
}
int main()
{
	cin>>n>>q;
	for(int i = 0; i < n; i++) cin>>a[i]; 
	sort(a, a + n);
	int ans = 1;
	for (int i = 0; i < n; i++)
	{
		int j = binarySearch(i,(long long)a[i] * q);
		ans = max(ans, j - i);
	}
	cout<<ans;
	return 0;
}

有點閱讀理解的味道,題目的意思:最大與最小相除小於p,求如何使數列中間的元素更多
分析:從最小數開始,不斷增大,找出 與 最大數開始往後減小(用二分法)的最下標大差值
補充:就當回顧二分法的適用題目與方法


1033 舊鍵盤打字

在這裏插入圖片描述

#include<iostream>
#include<ctype.h>
using namespace std;
int main()
{
	char a[128]={0},c;
	while((c=getchar())!='\n')
		a[c]=1;
	while((c=getchar())!='\n')
		if((isupper(c) && a[43]) || a[toupper(c)]==1) continue;
		else cout<<c;
    return 0;
}

題目和代碼本身簡單,但需要掌握兩個函數:isupper() 和 toupper()

isupper(c): 當參數c爲大寫英文字母(A-Z)時,返回 非0 值,否則返回 0

toupper(c): 能將字符c轉換爲大寫英文字母

兩者都包含於頭文件 <ctype.h>


1034 有理數四則運算

在這裏插入圖片描述

#include <iostream>
#include <cmath>
using namespace std;
long long a, b, c, d;
long long gcd(long long a, long long b)
{
    return b == 0 ? a : gcd(b, a % b);
}
void func(long long m, long long n)
{
    if (m * n == 0)
	{
        printf("%s", n == 0 ? "Inf" : "0"); //多掌握些這樣的用法
        return ;
    }
    bool flag = ((m < 0 && n > 0) || (m > 0 && n < 0));
    m = abs(m); n = abs(n);
    long long x = m / n;
    printf("%s", flag ? "(-" : "");
    if (x != 0) printf("%lld", x);
    if (m % n == 0)
	{
        if(flag) printf(")");
        return ;
    }
    if (x != 0) printf(" ");
    m = m - x * n;
    long long t = gcd(m, n);
    m = m / t; n = n / t;
    printf("%lld/%lld%s", m, n, flag ? ")" : "");
}
int main()
{
    scanf("%lld/%lld %lld/%lld", &a, &b, &c, &d);
    func(a, b); printf(" + "); func(c, d); printf(" = "); func(a * d + b * c, b * d); printf("\n");
    func(a, b); printf(" - "); func(c, d); printf(" = "); func(a * d - b * c, b * d); printf("\n");
    func(a, b); printf(" * "); func(c, d); printf(" = "); func(a * c, b * d); printf("\n");
    func(a, b); printf(" / "); func(c, d); printf(" = "); func(a * d, b * c);
    return 0;
}

分析func(m, n)的作用是對 m / n 的分數進行化簡
在 func()函數中,先看 m 和 n 裏面是否有0(m * n 是否等於0),再看 flag,flag=true表示 m 和 n 異號,那麼後面要添加”(-“”)”,再將 m和 n 取絕對值,然後計算 x = m/n,x 表示可提取的整數部分(帶分數),接着根據 m % n 是否等於 0 的結果判斷後面還有沒有小分數,若有小分數,然後求 m 和 n 的最大公約數 t ,讓 m 和 n 都除以 t 進行化簡,最後輸出即可

補充:判斷 m 和 n 是否異號千萬不要寫成判斷 m * n 是否小於 0,因爲 m * n 的結果可能超過了 long long int 的長度,導致溢出大於 0,如果這樣寫的話會有一個測試點無法通過


1035 插入與歸併

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int n, a[100], b[100], i, j;
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> a[i];
    for (int i = 0; i < n; i++)
        cin >> b[i];
    for (i = 0; i < n - 1 && b[i] <= b[i + 1]; i++);
	    for (j = i + 1; a[j] == b[j] && j < n; j++);
		    if (j == n)
			{
		        cout << "Insertion Sort" << endl;
		        sort(a, a + i + 2); //第二個參數 + 的是數量 
		    }
			else
			{
		        cout << "Merge Sort" << endl;
		        int k = 1, flag = 1;
		        while(flag) //直到符合 
				{
		            flag = 0;
		            for (i = 0; i < n; i++)
					{
		                if (a[i] != b[i])
		                    flag = 1;
		            }
		            k *= 2; //一次增一倍 
		            for (i = 0; i < n / k; i++)
		                sort(a + i * k, a + (i + 1) * k); //此處排列的方式是一次排一列 
		            sort(a + n / k * k, a + n); //將一列中剩下的元素也進行排列(代指餘數) 
		        }
		    }
    for (j = 0; j < n; j++)
	{
        if (j != 0) printf(" "); //類似於 firstblood 
        printf("%d", a[j]);
    }
    return 0;
}

先簡單回顧一下插入排序(圖1)歸併排序(圖2)
在這裏插入圖片描述
在這裏插入圖片描述
分析:先將 i 指向序列中滿足從左到右是從小到大順序的最後一個下標,再將 j 指向從 i + 1 開始,第一個不滿足a[j] == b[j]的下標,如果 j 順利到達了下標 n,說明是插入排序,否則說明是歸併排序

過程:如果是插入排序,再下一次的序列是sort(a, a+i+2)(這裏別忘了 sort 函數第二個參數 + 的是數量),而如果是歸併排序,直接對原來的序列進行模擬歸併過程

補充:插入排序是由前之後排序,歸併排序是整體排序,一次排一列


1039 到底買不買

在這裏插入圖片描述
在這裏插入圖片描述

#include<iostream>
using namespace std;
int main()
{
	int a[128]={0};
	char tmp;
    while((tmp=getchar())!='\n') //買 
    {
    	a[tmp]++;
	}
	while((tmp=getchar())!='\n') //想要 
    {
    	a[tmp]--;
	}
	int firstblood=1,votal1=0,votal2=0;
	for(int i=0;i<128;i++)
	{
		if(a[i]<0)
		{
			if(firstblood)
				firstblood=0;
			votal1+=-a[i]; //可以直接寫負數
		}
		else
		{
			votal2+=a[i];
		}
	}
	if(firstblood)
	{
		cout<<"Yes "<<votal2;
	}
	else
		cout<<"No "<<votal1;
    return 0;
}

比較奇特的一點是:由 一個數組 和 數組的各個值的正負 判斷 Yes or No
分析:想要記作 - - ,買了的記作++ ,那麼最後 正數即爲多餘的,負數即爲缺少的


1042 字符統計

在這裏插入圖片描述

#include<iostream>
#include<ctype.h>
using namespace std;
int main()
{
	int alpha[26]={0};
	char ch;
	while((ch=getchar())!='\n')
		if(isalpha(ch)) //判斷是否爲英文字母
			alpha[tolower(ch)-'a']++; //根據題意 ,只存小寫英文字母
	int _max=0,sp;
	for(int i=25;i>=0;i--)
		if(alpha[i]>=_max)
			sp=i,_max=alpha[i];
	cout<<char(sp+'a')<<' '<<_max; //用 char(int)的方式輸出字符
	return 0;
}

題目和代碼本身簡單,但需要掌握兩個函數:isalpha() 和 tolower()

isalpha(c): 當參數c爲英文字母 (A-Z) 或 (a-z) 時,返回 非0 值,否則返回 0

isalnum(c):當字符變量c爲字母或數字,返回 非0 值,否則返回 0

tolower(c): 能將字符c轉換爲小寫英文字母

兩者都包含於頭文件 <ctype.h>


1044 火星數字

在這裏插入圖片描述

#include <iostream>
#include <cctype>
#include <string>
using namespace std;
string a[13] = {"tret", "jan", "feb", "mar", "apr", "may", "jun", "jly", "aug", "sep", "oct", "nov", "dec"}; // 0到 12的火星文
string b[13] = {"####", "tam", "hel", "maa", "huh", "tou", "kes", "hei", "elo", "syy", "lok", "mer", "jou"}; //進位以後的 12個高位數字
string s;
int len;
void tomars(int t) //轉字符串
{
    if (t / 13) cout << b[t / 13]; //進位數 
    if ((t / 13) && (t % 13)) cout << " "; //有個位數 
    if (t % 13 || t == 0) cout << a[t % 13]; // 0 或 個位數 
}
void toearth() //轉數字 
{
    int t1 = 0, t2 = 0;
    string s1 = s.substr(0, 3), s2;
    if (len > 4) s2 = s.substr(4, 3);
    for (int j = 1; j < 13; j++)
	{
        if (s1 == a[j] || s2 == a[j]) t2 = j;
        if (s1 == b[j]) t1 = j;
    }
    cout << t1 * 13 + t2;
}
int main()
{
    int n;
    cin >> n;
    getchar(); //非常關鍵!
    for (int i = 0; i < n; i++)
	{
        getline(cin, s);
        len = s.length();
        if (isdigit(s[0]))
        {
        	int num=0;
    		for(int i=0;i<s.length();i++)
    			num=num*10+(s[i]-'0'); //注意不是 +=
    		tomars(num);
		}
        else
        	toearth();
        cout << endl;
    }
    return 0;
}

分析:本代碼用到了 string 數組,和各種各樣的函數

substr ( 開始,長度),相當於複製,舉例如下:

string s1 = s2.substr(0, 3);

atoi(c) 將字符 c 轉換成數字,舉例如下:

str[100]="00100" ; cout<<atoi(str)<<endl; output:100
//頭文件 <cstdlib>

strcmp(str1,str2),若str1=str2,則返回零;若str1<str2,則返回負數;若str1>str2,則返回正數
strcpy(str1,str2),將str2中的字符複製到str1

補充

string s;
getline(cin,s);
cout<<s.size();

//下面實例說明了空格在 getline中也算一個字符
input:a b
output:3

1045 快速排序

在這裏插入圖片描述

#include <iostream>
#include <algorithm>
using namespace std;
int v[100000];
using namespace std;
int main()
{
    int n, _max = 0, cnt = 0;
    cin>>n;
    int a[100005],b[100005];
    for (int i = 0; i < n; i++)
	{
        scanf("%d", &a[i]);
        b[i] = a[i];
    }
    sort(a,a+n);
    for (int i = 0; i < n; i++)
	{
        if(a[i] == b[i] && b[i] > _max)
            v[cnt++] = b[i];
        if (b[i] > _max)
            _max = b[i];
    }
    cout<<cnt<<endl;
    for(int i = 0; i < cnt; i++)
	{
        if (i != 0) cout<<' ';
        cout<<v[i];
    }
    cout<<endl;
    return 0;
}

主元的定義:比前面的數都大,比後面的數都小,而相當於主元本身已經排好序了,那麼將原數組排好序,對應位置相同的有可能是主元,再加上當前最大值的驗證方法,一重循環即可

然後我似乎也找到有些人熱衷於在末尾加換行符的原因了:不加就可能過不了測試點


1048 數字加密

在這裏插入圖片描述

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    string a, b, c;
    cin >> a >> b;
    int lena = a.length(), lenb = b.length();
    reverse(a.begin(), a.end()); //反轉字符串
    reverse(b.begin(), b.end());
    if (lena > lenb) 
        b.append(lena - lenb, '0');
    else
        a.append(lenb - lena, '0');
    char str[14] = {"0123456789JQK"};
    for (int i = 0; i < a.length(); i++)
	{
        if (i % 2 == 0)
			c += str[(a[i] - '0' + b[i] - '0') % 13];
		else
		{
            int temp = b[i] - a[i];
            if (temp < 0) temp = temp + 10;
            c += str[temp];
        }
    }
    for (int i = c.length() - 1; i >= 0; i--)
        cout << c[i];
    return 0;
}

題目和代碼本身簡單,但需要注意:題目並沒有說明白數字A可能比數字B長,這時需要短的數字補零(反轉字符串即可)

補充:關於 string類append 的用法,舉例如下:

string a, b;
cin >> a >> b;
int lena = a.length(), lenb = b.length();
if (lena > lenb) //若 a 比 b 長,就補齊 b
	b.append(lena - lenb, '0'); //第一個參數是個數,第二個參數是補充的字符
cout << b;

input:12345 123
output:12300

1049 數列的片段和

在這裏插入圖片描述

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
    int n;
    cin >> n;
    double sum = 0.0, temp;
    for (int i = 1; i <= n; i++)
    { 
        cin >> temp;
        sum += temp * i * (n - i + 1);
    }
    printf("%.2f", sum);
    return 0;
}

單純就是數學題,每個數出現的個數是(左邊數的個數+1)*(右邊數的個數+1)


1050 螺旋矩陣

在這裏插入圖片描述

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
int cmp(int a, int b) {return a > b;}
int main()
{
    int N, m, n, t = 0; //t代表總個數-1 
    scanf("%d", &N);
    for (n = sqrt((double)N); n >= 1; n--) //計算矩陣(sqrt只適用於浮點型) 
	{
        if (N % n == 0)
		{
            m = N / n;
            break;
        }
    }
    vector<int> a(N); //動態數組
    for (int i = 0; i < N; i++)
        scanf("%d", &a[i]);
    sort(a.begin(), a.end(), cmp);
    vector<vector<int> > b(m, vector<int>(n)); //二維動態數組 
    int level = m / 2 + m % 2; // level層數算法 
    for (int i = 0; i < level; i++) //矩陣排序
	{
        for (int j = i; j <= n - 1 - i && t <= N - 1; j++) 
                b[i][j] = a[t++];
        for (int j = i + 1; j <= m - 2 - i && t <= N - 1; j++)
                b[j][n - 1 - i] = a[t++];
        for (int j = n - i - 1; j >= i && t <= N - 1; j--)
                b[m - 1 - i][j] = a[t++];
        for (int j = m - 2 - i; j >= i + 1 && t <= N - 1; j--)
                b[j][i] = a[t++];
    }
    for (int i = 0; i < m; i++)
	{
        for (int j = 0 ; j < n; j++)
		{
            printf("%d", b[i][j]);
            if (j != n - 1) printf(" ");
        }
        printf("\n");
    }
    return 0;
}

解析:首先計算行數m列數n的值,題目要求(m >= n),n 等於根號N的整數部分一直往 1 減,直到 N % n == 0,m 等於 N / n,再算 層數level(儘量多),等於 m / 2 + m % 2(所以用 m),最後矩陣排序,按順時針螺旋方向,數字從大到小,輸入數組元素,最後正常輸出數組即可

補充:輸入數組元素時,按照 4 個 for 循環輸入,注意第二個和第四個 for 循環的起始位置和結束位置(輸入元素總量少一個)
在這裏插入圖片描述
不想佔用棧太多內存時,可以用 vector 充當動態數組,因爲其數據存儲在堆中,用法如下:

//建立一維動態數組
vector<int> a(10);
//建立二維動態數組
vector<vector<int> > b(10, vector<int>(10));
//輸入
cin>>a[0]>>b[0][0];
//輸出
cout<<a[0]<<' '<<b[0][0];

input:5 5
output:5 5

1051 複數乘法

在這裏插入圖片描述

#include <cstdio>
#include <cmath>
int main() {
	double a,b,c,d;
	scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
	double a1,a2;
	a1=a*c*cos(b+d);//三角複合函數
	a2=a*c*sin(b+d);//三角複合函數
	if (fabs(a1)<=0.01)
		a1=0.00;
	if (fabs(a2)<=0.01)
		a2=0.00;
	if (a2<0)
		printf("%.2lf-%.2lfi",a1,-a2);
	else
		printf("%.2lf+%.2lfi",a1,a2);
	return 0;
}

題目可能有問題,沒有說清楚要不要四捨五入,順便補充一個關於四捨五入小知識點

double a=0.004;
double b=0.005;
printf("%.2lf\n",a);
printf("%.2lf",b);

//從結果來看,對於格式化,程序會自動四捨五入
output:0.00
	   0.01

1052 賣個萌

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <vector>
using namespace std;
int main()
{
    vector<vector<string> > v; //二重動態數組 
    for(int i = 0; i < 3; i++)
	{
        string s;
        getline(cin, s);
        vector<string> row;
        int j = 0, k = 0;
        while(j < s.length())
		{
            if(s[j] == '[')
			{
                while(k++ < s.length())
				{
                    if(s[k] == ']')
					{
                        row.push_back(s.substr(j+1, k-j-1)); //(長度,開始)
                        break;
                    }
                }
            }
            j++;
        }
        v.push_back(row);
    }
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
	{
        int a, b, c, d, e;
        cin >> a >> b >> c >> d >> e;
        if(a > v[0].size() || b > v[1].size() || c > v[2].size() || d > v[1].size() || e > v[0].size() || a < 1 || b < 1 || c < 1 || d < 1 || e < 1)
		{
            cout << "Are you kidding me? @\\/@" << endl;
            continue;
        }
        cout << v[0][a-1] << "(" << v[1][b-1] << v[2][c-1] << v[1][d-1] << ")" << v[0][e-1] << endl; //注意減一 
    }
    return 0;
}

問題和代碼本身不難,但用到了二重動態規劃(string類型),舉例如下:

vector<vector<string> > v; //注意對於 string 類型不用設置長度
vector<string> row;
string a;
cin>>a;
row.push_back(a);
v.push_back(row);
cout<<v[0][0];

input:12345
output:12345

1054 求平均值

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <cstdio>
#include <string.h>
using namespace std;
int main()
{
    int n, cnt = 0;
    char a[50], b[50];
    double temp, sum = 0.0;
    cin >> n;
    for(int i = 0; i < n; i++)
	{
        scanf("%s", a);
        sscanf(a, "%lf", &temp); //將 a 輸入到 temp 中
        sprintf(b, "%.2f",temp); //將 temp 輸入到 b 中
        int flag = 0;
        for(int j = 0; j < strlen(a); j++) //以 a 爲參照物進行篩選排除,很關鍵的一步操作
            if(a[j] != b[j]) flag = 1;
        if(flag || temp < -1000 || temp > 1000)
		{
            printf("ERROR: %s is not a legal number\n", a);
            continue;
        }
		else
		{
            sum += temp;
            cnt++;
        }
    }
    if(cnt == 1)
        printf("The average of 1 number is %.2f", sum);
    else if(cnt > 1)
        printf("The average of %d numbers is %.2f", cnt, sum / cnt);
    else
        printf("The average of 0 numbers is Undefined");
    return 0;
}

先講解 sscanfsprintf 的作用:
sscanf(a,"%lf",&temp):從 a(字符串)中讀進與指定格式相符的數據輸入到 temp 中
sprintf(b,"%.2f",temp):將 temp 格式化輸入到 b(字符串)中 (注意沒有&號)

下面以部分本代碼舉例:

char a[50], b[50];
double temp;
scanf("%s", a);
sscanf(a, "%lf", &temp);
sprintf(b, "%.2f",temp);
cout<<a<<endl<<temp<<'  '<<b;

//從下列實例看出排除了多種不合法數據
input:1.23.4
ouput:1.23  1.23
input:45.678
ouput:45.678  45.68
input:aaa
ouput:4.94066e-324  0.00

以其他例子舉例:

char str[100]="1234:3.14,hello",arr[10];
int n;
double db;
sscanf(str,"%d:%lf,%s",&n,&db,arr); //注意這裏不能用 %.2lf(似乎是規定)
cout<<n<<' '<<db<<' '<<arr<<endl;

char str2[100],arr2[]="hello";
int n2=1234;
double db2=3.14;
sprintf(str2,"%d:%.2lf,%s",n2,db2,arr2);
cout<<str2;

//下面實例說明此函數可以轉換多個參數
output:
1234 3.14 hello
1234:3.14,hello

1055 集體照

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct node
{
    string name;
    int height;
};
int cmp(struct node a, struct node b)
{
    return a.height != b.height ? a.height > b.height : a.name < b.name; //掌握這種表達方法 
}
int main()
{
    int n, k, m; //人數 排數 某一行 
    cin >> n >> k;
    vector<node> stu(n); //直接用 vector 容器代替了數組 
    for(int i = 0; i < n; i++)
	{
        cin >> stu[i].name >> stu[i].height;
    }
    sort(stu.begin(), stu.end(), cmp);
    int t = 0, row = k; // t代表排序進行的位置 row代表當前行數 
    while(row--)
	{
        if(row == k-1) //最後一排
            m = n - n / k * (k - 1);
        else
            m = n / k;
        vector<string> ans(m);
        ans[m / 2] = stu[t].name;
        // 左邊一列
        int j = m / 2 - 1;
        for(int i = t + 1; i < t + m; i = i + 2)
            ans[j--] = stu[i].name;
        // 右邊一列
        j = m / 2 + 1;
        for(int i = t + 2; i < t + m; i = i + 2)
            ans[j++] = stu[i].name;
        // 輸出當前排
        cout << ans[0];
        for(int i = 1; i < m; i++)
            cout << " " << ans[i];
        cout << endl;
        t = t + m;
    }
    return 0;
}

分析:因爲是面對拍照者,後排的人輸出在上方,前排輸出在下方,每排人數爲 N / K,多出來的人全部站在最後一排,最中間一個學生應該排在 m / 2 的下標位置(題目的要求有歧義),即ans[m / 2] = stu[t].name,然後排左邊一列,ans 數組的下標 j 從 m / 2 - 1 開始,一直往左 j - -,而對於 stu 的下標 i 從 t + 1 開始,每次隔一個人選取,排右邊的隊伍同理,最後輸出當前已經排好的 ans 數組


1056 組合數的和

在這裏插入圖片描述

#include <cstdio>
#include<iostream>
using namespace std;
int main()
{
	int N, sum = 0, temp;
	scanf("%d", &N);
	for (int i = 0; i < N; i++)
	{
		scanf("%d", &temp);
		sum += temp * 10 * (N - 1) + temp * (N - 1);
	}
	printf("%d", sum);
	return 0;
}

技巧性題目,在 sum 累加的過程中,對於每一個輸入的數字 temp,能夠放在個位也能夠放在十位,所以每個數字 temp 都能在個位出現 (N-1) 次,十位出現 (N-1) 次,在個位產生的累加效果爲 temp * (N-1) ,而在十位產生的累加效果爲 temp * (N-1) * 10,所以 sum 即是答案


1058 選擇題

在這裏插入圖片描述

#include <cstdio>
#include <vector>
#include <set>
using namespace std;
int main()
{
    int n, m, temp, k; //學生人數 多選題的個數 選項個數 正確選項個數 
    scanf("%d%d", &n, &m);
    vector<set<char> > right(m); //題目選項 
    vector<int> total(m), wrongCnt(m); //每一題的總分 某題錯誤的人數 
    for(int i = 0; i < m; i++)
	{
        scanf("%d%d%d", &total[i], &temp, &k);
        for(int j = 0; j < k; j++)
		{
            char c;
            scanf(" %c", &c);
            right[i].insert(c);
        }
    }
    for(int i = 0; i < n; i++)
	{
        int score = 0;
        scanf("\n"); //表示忽略回車鍵 
        for(int j = 0; j < m; j++)
		{
            if(j != 0) scanf(" ");
            scanf("(%d", &k);
            set<char> st;
            char c;
            for(int l = 0; l < k; l++)
			{
                scanf(" %c", &c);
                st.insert(c);
            }
            scanf(")");
            if(st == right[j]) //st集合和該題目選項一致 
                score += total[j];
			else
                wrongCnt[j]++;
        }
        printf("%d\n", score);
    }
    int maxWrongCnt = 0;
    for(int i = 0; i < m; i++)
	    maxWrongCnt=max(maxWrongCnt,wrongCnt[i]);
    if(maxWrongCnt == 0)
        printf("Too simple");
    else
	{
        printf("%d", maxWrongCnt);
        for(int i = 0; i < m; i++)
            if(wrongCnt[i] == maxWrongCnt) //輸入編號 
                printf(" %d", i + 1);
    }
    return 0;
}

分析:本題只要思路明確就可以很容易寫出代碼,要求輸出 每個人的分數 和 錯誤人數最多的題和編號,那麼肯定要設置 wrongCnt[i] 作爲每題錯誤人數,maxWrongCnt作爲錯誤最多的人數,再根據題目要求設置相應的變量進行推導即可
解析:set 是一種特別好的容器,它能存儲元素各不同元素作爲集合,舉例如下:

set<int> st,sp;

//判斷兩種集合包含元素是否相同 (直接用等號判斷)
st==sp;

//插入 (括號內是元素值)
st.insert(5)

//查找 (括號內是元素值;若找不到則返回 st.end())
st.find(5)!=st.end()

//是否存在 (括號內是元素值;若找到返回非 0值;若找不到則返回 0)
st.count(5)

小知識:vector 封裝數組,set 封裝集合,queue 封裝隊列,list 封裝鏈表,map 封裝關聯數
補充:關於 scanf()另類的用法,舉例如下:

int d;
scanf("("); //表示忽略左括號
scanf("%d",&d);
scanf(")"); //表示忽略右括號
cout<<d;

//下面實例論證上述說明是正確的
input: (45)
output: 45

input: 45
output: 45

input: )45(
output: 0

1059 C語言競賽

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <set>
#include <cmath>
using namespace std;
int ran[10000];
bool isprime(int a)
{
    if(a <= 1) return false;
    for(int i = 2; i <= sqrt((double)a); i++)
        if(a % i == 0)
            return false;
    return true;
}
int main()
{
    int n, k;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
	{
        int id;
        scanf("%d", &id);
        ran[id] = i + 1;
    }
    scanf("%d", &k);
    set<int> ss;
    for(int i = 0; i < k; i++)
	{
        int id;
        scanf("%d", &id);
        printf("%04d: ", id);
        if(ran[id] == 0)
		{
            printf("Are you kidding?\n");
            continue;
        }
        if(ss.find(id) == ss.end())
            ss.insert(id);
		else
		{
            printf("Checked\n");
            continue;
        }
        if(ran[id] == 1)
            printf("Mystery Award\n");
		else if(isprime(ran[id]))
            printf("Minion\n");
		else
            printf("Chocolate\n");
    }
    return 0;
}

題目和代碼本身不難,就是想提醒一下以後遇到素數不要老想着用歐拉線性篩法,記一下普通模板

bool isprime(int a)
{
    if(a <= 1) return false;
    for(int i = 2; i <= sqrt((double)a); i++)
        if(a % i == 0)
            return false;
    return true;
}

1060 愛丁頓數

在這裏插入圖片描述

#include <iostream>
#include <algorithm>
using namespace std;
int a[100005];
bool cmp(int a, int b)
{
	return a > b;
}
int main()
{
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]); //注意要從 1 開始,因爲天數不能爲 0
    sort(a+1, a+n+1, cmp);
    int ans = 0, p = 1;
    while(ans < n && a[p] > p)
	{
        ans++;
        p++;
    }
    printf("%d", ans);
    return 0;
}

單純就是考數學,從下標 1 開始存儲 n 天的公里數在數組 a 中,對 n 個數據從大到小排序,i 表示了騎車的天數,那麼滿足a[i] > i的最大值即答案


1064 朋友數

在這裏插入圖片描述

#include <iostream>
#include <set>
using namespace std;
int getFriendNum(int num)
{
    int sum = 0;
    while(num != 0)
	{
        sum += num % 10;
        num /= 10;
    }
    return sum;
}
int main()
{
    set<int> s;
    int n, num;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
	{
        scanf("%d", &num);
        s.insert(getFriendNum(num));
    }
    printf("%d\n", s.size());
    set<int>::iterator it; //迭代器
    for(it = s.begin(); it != s.end(); it++)
	{
        if(it != s.begin()) printf(" "); //與 firstblood神似
        printf("%d", *it);
    }
    return 0;
}

題目和代碼本身不難,但補充幾個有關 set 的小知識點:

//1 定義時不用寫集合個數,因爲不常用到下標
set<int> s;

//2 關於 int型的元素會自動排序,不用手動 sort

//3 輸出 set元素不用下標法,用如下方法
for(auto it = s.begin(); it != s.end(); it++) //auto 非常好用,但只適用於 PAT

set<int>::iterator it; //迭代器
for(it = s.begin(); it != s.end(); it++) //藍橋杯和 PAT都適用

//也是關於藍橋杯和 PAT的
stoi()

//此函數僅適用於 PAT
string str = "010";
int a = stoi(str, 0, 2); //將 2進制轉換成 10進制
int b = stoi(str, 0, 3); //將 3進制轉換成 10進制
cout << a << endl;
cout << b << endl;

//中間的參數是起始位置,但最好別改(改了會報錯,我也不知道怎麼回事)
output:
2
3

1067 試密碼

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
using namespace std;
int main()
{
    string password, temp;
    int n, cnt = 0;
    cin >> password >> n;
    getchar(); //當後面有 getline時必要的操作 
    while(1)
	{
        getline(cin, temp);
        if (temp == "#") break;
        cnt++;
        if (cnt <= n && temp == password)
		{
            cout << "Welcome in";
            break;
        }
		else if (cnt <= n && temp != password)
		{
            cout << "Wrong password: " << temp << endl;
            if (cnt == n)
			{
                cout << "Account locked";
                break;
            }
        }
    }
    return 0;
}

題目和代碼本身簡單,但想回顧一下字符串的邊界與 getchar()的必要性


1068 萬綠叢中一點紅

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <vector>
#include <map>
using namespace std;
int m, n, tol;
vector<vector<int> > v;
int dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};
bool judge(int i, int j)
{
    for (int k = 0; k < 8; k++) //八種情況
	{
        int tx = i + dir[k][0];
        int ty = j + dir[k][1];
        if (tx >= 0 && tx < n && ty >= 0 && ty < m && v[i][j] - v[tx][ty] >= 0 - tol && v[i][j] - v[tx][ty] <= tol) return false;
    }
    return true;
}
int main()
{
    int cnt = 0, x = 0, y = 0;
    scanf("%d%d%d", &m, &n, &tol); //橫座標 縱座標 差值
    v.resize(n, vector<int>(m));
    map<int, int> mapp;
    for (int i = 0; i < n; i++)
	{
        for (int j = 0; j < m; j++)
		{
            scanf("%d", &v[i][j]);
            mapp[v[i][j]]++;
        }
    }
    for (int i = 0; i < n; i++)
	{
        for (int j = 0; j < m; j++)
		{
            if (mapp[v[i][j]] == 1 && judge(i, j) == true)
			{
                cnt++;
                x = i + 1;
                y = j + 1;
            }
        }
    }
    if (cnt == 1)
        printf("(%d, %d): %d", y, x, v[x-1][y-1]);
    else if (cnt == 0)
        printf("Not Exist");
    else
        printf("Not Unique");
    return 0;
}

分析:思路大致和迷宮問題類似,但此題有個額外注意的地方,即判定條件,僅當滿足在 m座標 與 n座標內 和 差值在200之間才判定爲否,其他均爲正確,這樣做的好處就是判斷越界的元素也是對的(相當於在邊界的元素只用判定相鄰的元素即可)

補充vector 二維數組的兩種表達方式,舉例如下:

int n=5,m=5;

//第一種
vector<vector<int> > v(n, vector<int>(m));

//第二種 (安全些,不容易發生段錯誤)
vector<vector<int> > v;
v.resize(n, vector<int>(m));

額外:vector 的 resize 另一種用法

補充:有關 map 的用法,舉例如下:

map<int,int> m;
m[1000]++;
m[100]++;
map<int,int>::iterator it;
for(it=m.begin();it!=m.end();it++)
	cout << (*it).first << " " << (*it).second << endl;

//說明 map可直接用來存儲關聯數,並可以有效提取
output:
100 1
1000 1

m[1]=5,m[2]=4,m[3]=3,m[4]=2,m[5]=1;
map<int,int>::iterator it;
for(it=m.begin();it!=m.end();it++){
	cout << (*it).first << " " << (*it).second << endl;

//再一次論證
output:
1 5
2 4
3 3
4 2
5 1

1069 微博轉發抽獎

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <map>
using namespace std;
int main()
{
    int m, n, s;
    scanf("%d%d%d", &m, &n, &s); //轉發的總量 中獎間隔 第一位中獎者序號
    string str;
    map<string, int> mapp; //新用法
    bool flag = false;
    for (int i = 1; i <= m; i++)
	{
        cin >> str;
        if (mapp[str] == 1) s = s + 1; //寫在循環前面
        if (i == s && mapp[str] == 0)
		{
            mapp[str] = 1;
            cout << str << endl;
            flag = true;
            s = s + n;
        }
    }
    if (flag == false) cout << "Keep going...";
    return 0;
}

這個代碼很機智的將複雜簡化了,若是一般寫容易i+n,但本代碼將全部循環寫出來,並用另一個變量 s 代替 + n,這樣就避免代碼複雜化


1070 結繩

在這裏插入圖片描述

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
    int n;
    scanf("%d", &n);
    vector<int> v(n);
    for (int i = 0; i < n; i++)
        scanf("%d", &v[i]);
    sort(v.begin(), v.end());
    int result = v[0];
    for (int i = 1; i < n; i++)
        result = (result + v[i]) / 2;
    printf("%d", result);
    return 0;
}

數學題,因爲所有長度都要串在一起,所以越早串繩子,越要對摺的次數多,所以既然希望繩子長度是最長的,就必須讓長的段對摺次數儘可能的短


1073 多選題常見計分法

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
int main()
{
    int n, m, optnum, truenum, temp, maxcnt = 0;
    int hash[] = {1, 2, 4, 8, 16}, opt[1005][105] = {0}; //每一個選項 學生選項 
    char c;
    scanf("%d %d", &n, &m); //學生人數 多選題的個數
    vector<int> fullscore(m), trueopt(m); //滿分值 正確選項  
    vector<vector<int> > cnt(m, vector<int>(5)); //錯誤次數
    for (int i = 0; i < m; i++)
	{
        scanf("%d %d %d", &fullscore[i], &optnum, &truenum); //滿分值 選項個數(沒用) 正確選項個數
        for (int j = 0; j < truenum; j++) 
		{
            scanf(" %c", &c); //所有正確選項
            trueopt[i] += hash[c-'a'];
        }
    }
    for (int i = 0; i < n; i++)
	{
        double grade = 0; 
        for (int j = 0; j < m; j++)
		{
            scanf("\n");
            scanf("(%d", &temp);
            for (int k = 0; k < temp; k++)
			{
                scanf(" %c", &c);
                opt[i][j] += hash[c-'a'];
            }
            scanf(")", &c);
            int el = opt[i][j] ^ trueopt[j]; //異或
            if (el)
			{
                if ((opt[i][j] | trueopt[j]) == trueopt[j])
                    grade += fullscore[j] * 1.0 / 2; //漏選 得一半分 
                if (el)
                    for (int k = 0; k < 5; k++)
                        if (el & hash[k]) cnt[j][k]++; //記錄錯誤次數 
            }
			else
                grade += fullscore[j];
        }
        printf("%.1f\n", grade); //學生得分 
    }
    for (int i = 0; i < m; i++)
        for (int j = 0; j < 5; j++)
            maxcnt = max(maxcnt,cnt[i][j]); //最大錯誤次數
    
    if (maxcnt == 0)
        printf("Too simple\n");
	else
        for (int i = 0; i < m; i++)
            for (int j = 0; j < 5; j++)
                if (maxcnt == cnt[i][j])
                    printf("%d %d-%c\n", maxcnt, i+1, 'a'+j); //錯誤次數 題目編號 選項(字母)
    return 0;
}

分析:用異或運算來判斷一個選項和正確選項是否匹配,如果是匹配的,那麼異或的結果應當是0,如果不匹配,那麼這個選項就是存在錯選或者漏選的情況,例如:設 a 爲 00001,b 爲 00010,c 爲 00100,d 爲 01000,e 爲 10000,如果給定的正確答案是 ac,即 10001,那麼如果給出的選項也是 10001,異或的結果就是 0,如果給出的選項是 a(漏選)或者 ab(選錯),即 00001 或 00011,異或之後皆不爲 0,通過 異或操作的結果與運算符 就可以把錯選和漏選的選項找出來,本代碼 存儲選項的變量 存儲的是二進制值,二進制由 hash 給出分別是1 2 4 8 16,即二進制各位置的 1(本題需要設置的變量特別多)

補充

運算符 描述
& 按位與運算符: 參與運算的兩個值,如果兩個相應位都爲1,則該位的結果爲1,否則爲0
| 按位或運算符: 只要對應的二個二進位有一個爲1時,結果位就爲1。
^ 按位異或運算符: 當兩對應的二進位相異時,結果爲1
~ 按位取反運算符: 對數據的每個二進制位取反,即把1變爲0,把0變爲1 ;~x 類似於 -x-1
<< 左移動運算符: 運算數的各二進位全部左移若干位,由 << 右邊的數字指定了移動的位數,高位丟棄,低位補0。
>> 右移動運算符: 把">>"左邊的運算數的各二進位全部右移若干位,>> 右邊的數字指定了移動的位數

下面以變量 a 爲 60,b 爲 13 舉例,二進制格式如下:

a = 0011 1100

b = 0000 1101

-----------------

a & b = 0000 1100

a | b = 0011 1101

a ^ b = 0011 0001

~a  = 1100 0011

a << 2 = 1111 0000

a >> 2 = 0000 1111

1074 宇宙無敵加法器

在這裏插入圖片描述

#include <iostream>
using namespace std;
int main()
{
    string s, s1, s2, ans;
    int carry = 0, flag = 0;
    cin >> s >> s1 >> s2;
    ans = s;
    string ss1(s.length() - s1.length(), '0'); //長度補齊 
    s1 = ss1 + s1;
    string ss2(s.length() - s2.length(), '0'); //長度補齊 
    s2 = ss2 + s2;
    for(int i = s.length() - 1; i >= 0; i--)
	{
        int mod = s[i] == '0' ? 10 : (s[i] - '0');
        ans[i] = (s1[i] - '0' + s2[i] - '0' + carry) % mod + '0'; //當前位
        carry = (s1[i] - '0' + s2[i] - '0' + carry) / mod; //進制位
    }
    if (carry != 0) ans = '1' + ans;
    for(int i = 0; i < ans.size(); i++)
	{
        if (ans[i] != '0' || flag == 1 || carry != 0)
		{
            flag = 1;
            cout << ans[i];
        }
    }
    if (flag == 0) cout << 0;
    return 0;
}

代碼非常簡潔,但沒什麼好講的,回顧一下 string 類進制


1075 鏈表元素分類

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <vector>
using namespace std;
struct node
{
    int data, next;
}list[100000];
vector<int> v[3]; //很棒的選擇 分三類 
int main()
{
    int start, n, k, a;
    scanf("%d%d%d", &start, &n, &k);
    for (int i = 0; i < n; i++)
	{
        scanf("%d", &a);
        scanf("%d%d", &list[a].data, &list[a].next);
    }
    while(start != -1)
	{
        int data = list[start].data;
        if (data < 0) //在選擇的類別裏面尾插 
            v[0].push_back(start);
        else if (data >= 0 && data <= k)
            v[1].push_back(start);
        else
            v[2].push_back(start);
        start = list[start].next;
    }
    int flag = 0;
    for (int i = 0; i < 3; i++)
	{
        for (int j = 0; j < v[i].size(); j++)
		{
            if (flag == 0)
			{
                printf("%05d %d ", v[i][j], list[v[i][j]].data);
                flag = 1;
            }
			else
                printf("%05d\n%05d %d ", v[i][j], v[i][j], list[v[i][j]].data);
        }
    }
    printf("-1");
    return 0;
}

此題與1025類似,但那題用的是堆數組,而此題用的是 vector 數組,作用就是可以根據題目要求分三類,而要插入直接 push_back,非常的簡潔(那題只用分一類,不用多此一舉)


1076 Wifi密碼

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
using namespace std;
int main()
{
    string s;
    while (cin >> s) 
        if(s.size() == 3 && s[2] == 'T') cout << s[0]-'A'+1;
    return 0;
}

分析:超精簡代碼,n 沒什麼作用,以字符串方式接收輸入,只要遇到任何一個字符串 s 滿足大小爲 3 且 s[2] 爲 ‘T’,就將 s[0] 字母對應的 wifi 密碼輸出


1080 MOOC期終成績

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
struct node
{
    string name;
    int gp, gm, gf, g; //編程 期中 期末 總評 
};
bool cmp(node a, node b)
{
    return a.g != b.g ? a.g > b.g : a.name < b.name;
}
map<string, int> idx; //用於記錄排除編程後的結構體順序 
int main()
{
    int p, m, n, score, cnt = 1;
    cin >> p >> m >> n;
    vector<node> v, ans; //初始動態結構體數組 最終動態結構體數組 
    string s;
    for (int i = 0; i < p; i++)
	{
        cin >> s >> score;
        if (score >= 200) //先實施排除編程 
		{
            v.push_back(node{s, score, -1, -1, 0}); //尾插結構體 
            idx[s] = cnt++;
        }
    }
    for (int i = 0; i < m; i++)
	{
        cin >> s >> score;
        if (idx[s] != 0) v[idx[s] - 1].gm = score; //記錄期中 
    }
    for (int i = 0; i < n; i++)
	{
        cin >> s >> score;
        if (idx[s] != 0)
		{
            int temp = idx[s] - 1; //順序成員 
            v[temp].gf = v[temp].g = score; //先使總評爲期末 
            if (v[temp].gm > v[temp].gf) v[temp].g = int(v[temp].gm * 0.4 + v[temp].gf * 0.6 + 0.5); //總評變化 
        }
    }
    for (int i = 0; i < v.size(); i++)
        if (v[i].g >= 60) ans.push_back(v[i]); //最後實施排除總評 
    sort(ans.begin(), ans.end(), cmp);
    for (int i = 0; i < ans.size(); i++)
        printf("%s %d %d %d %d\n", ans[i].name.c_str(), ans[i].gp, ans[i].gm, ans[i].gf, ans[i].g);
    return 0;
}

分析
1、因爲所有人必須要G編程>=200分,所以用v數組保存所有G編程>=200的人,(一開始gm和gf都爲-1),用map映射保存名字所對應v中的下標(爲了避免與“不存在”混淆,保存下標+1,當爲0時表示該學生的姓名在v中不存在)
2、G期中中出現的名字,如果對應的map並不存在(==0),說明該學生編程成績不滿足條件,則無須保存該學生信息。將存在的人的期中考試成績更新
3、G期末中出現的名字,也必須保證在map中存在,先更新G期末和G總爲新的成績,當G期末<G期中時再將G總更新爲(G期中x 40% + G期末x 60%)
4、將v數組中所有G總滿足條件的放入ans數組中,對ans排序(總分遞減,總分相同則姓名遞增),最後輸出ans中的學生信息


1084 外觀數列

在這裏插入圖片描述

#include <iostream>
using namespace std;
int main()
{
    string s;
    int n, j;
    cin >> s >> n;
    for (int cnt = 1; cnt < n; cnt++)
	{
        string t;
        for (int i = 0; i < s.length(); i = j)
		{
            for (j = i; j < s.length() && s[j] == s[i]; j++); //注意是讓 for循環單獨循環下去 
            t += s[i] + to_string(j - i);
        }
        s = t;
    }
    cout << s;
    return 0;
}

分析:題目的意思是 d 代表某個數字來進行數列演示,而關於代碼的關鍵部分,可以自行想象數字(字符串)迭代的過程

補充:

to_string()函數作用:將數字常量轉換成 string 類
注意:此函數在 PTA 可以使用,而藍橋杯不允許

若要用可以使用代替的函數表示,舉例如下:

string  to_string(int a)
{
	string b="";
	char c[1000]={0}; //存儲空間根據題目調整 
	sprintf(c,"%d",a);
	for(int i=0;i<strlen(c);i++)
		b+=c[i];
	return b;
}
//注意還有頭文件 <cstring> 和 <cstdio>

1085 PAT單位排行

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <algorithm>
#include <cctype>
#include <vector>
#include <unordered_map>
using namespace std;
struct node
{
    string school;
    int tws, ns; //分數 人數
};
bool cmp(node a, node b)
{
    if (a.tws != b.tws)
        return a.tws > b.tws;
    else if (a.ns != b.ns)
        return a.ns < b.ns;
    else
        return a.school < b.school; //string 可以按 ASCII碼排序
}
int main()
{
    int n;
    scanf("%d", &n);
    unordered_map<string, int> cnt; //人數 
    unordered_map<string, double> sum; //加權分數 
    for (int i = 0; i < n; i++)
	{
        string id, school;
        cin >> id;
        double score;
        scanf("%lf", &score);
        cin >> school;
        for (int j = 0; j < school.length(); j++)
            school[j] = tolower(school[j]);
        if (id[0] == 'B')
            score = score / 1.5;
        else if (id[0] == 'T')
            score = score * 1.5;
        sum[school] += score;
        cnt[school]++;
    }
    vector<node> ans;
    for (auto it = cnt.begin(); it != cnt.end(); it++)
        ans.push_back(node{it->first, (int)sum[it->first], cnt[it->first]});  //新數組接收
    sort(ans.begin(), ans.end(), cmp);
    int rank = 0, pres = -1;
    printf("%d\n", (int)ans.size());
    for (int i = 0; i < ans.size(); i++)
	{
        if (pres != ans[i].tws) rank = i + 1; //處理並列排名的妙招 
        pres = ans[i].tws;
        printf("%d ", rank);
        cout << ans[i].school;
        printf(" %d %d\n", ans[i].tws, ans[i].ns);
    }
    return 0;
}

分析:兩個 map,一個 cnt 用來存儲某學校名稱對應的參賽人數,另一個 sum 計算某學校名稱對應的總加權成績,每次學校名稱 string school 都要轉化爲全小寫,將 map 中所有學校都保存在 vector ans 中,類型爲 node,node 中包括學校姓名、加權總分、參賽人數,對 ans 數組排序,根據題目要求寫好 cmp 函數,最後按要求輸出,對於排名的處理:設立 pres 表示前一個學校的加權總分,如果 pres 和當前學校的加權總分不同,說明 rank 等於數組下標 + 1,否則 rank不變

補充

本代碼用的頭文件 <unordered_map> 僅適用於 PTA
而藍橋杯只用用 <map>

同理,藍橋杯不能用 auto ,要轉爲迭代器類型

1086 就不告訴你

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
    int a, b;
    scanf("%d %d", &a, &b);
    string s = to_string(a * b);
    reverse(s.begin(), s.end());
    printf("%d", stoi(s));
    return 0;
}

單純回顧一下 string 類 既有 to_string 又有 stoi 還可以 reverse


1089 狼人殺-簡單版

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int main()
{
    int n;
    cin >> n;
    vector<int> v(n+1);
    for (int i = 1; i <= n; i++) cin >> v[i]; //每個人說的話 
    for (int i = 1; i < n; i++)
	{
        for (int j = i + 1; j <= n; j++)
		{
            vector<int> lie, a(n + 1, 1); //lie 數組代表說謊的人 ,a 數組代表真實情況 ,好人全賦值爲 1 
            a[i] = a[j] = -1; //i j 爲壞人 ,賦值爲 -1 
            for (int k = 1; k <= n; k++)
                if (v[k] * a[abs(v[k])] < 0) lie.push_back(k); 
            if (lie.size() == 2 && a[lie[0]] + a[lie[1]] == 0)
			{
                cout << i << " " << j;
                return 0;
            }
        }
    }
    cout << "No Solution";
    return 0;
}

分析:題目:已知 N 名玩家中有 2 人扮演狼人角色,有 2 人說的不是實話,有狼人撒謊但並不是所有狼人都在撒謊,說明這兩個說謊的人一個是好人一個是狼人,每個人說的數字保存在 v 數組中,i 從 1~n - 1 、j 從 i + 1~n 遍歷,分別假設 i 和 j 是狼人,a 數組表示真實情況(該人是狼人還是好人),等於 1 表示是好人,等於 -1 表示是狼人。k 從 1~n 分別判斷 k 所說的話是真是假,k 說的話和真實情況不同(即v[k] * a[abs(v[k])] < 0)則表示 k 在說謊,則將 k 放在 lie 數組中,遍歷完成後判斷 lie 數組,如果說謊人數等於 2 並且這兩個說謊的人一個是好人一個是狼人(即a[lie[0]] + a[lie[1]] == 0)表示滿足題意,此時輸出 i 和 j 並 return,否則最後的時候輸出 No Solution


1090 危險品裝箱

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{
    int n, k, t1, t2;
    map<int,vector<int>> m; //map 的又一種用法 
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++) //map 不止一一對應 
	{
        scanf("%d%d", &t1, &t2);
        m[t1].push_back(t2);
        m[t2].push_back(t1);
    }
    while (k--)
	{
        int cnt, flag = 0, a[100000] = {0};
        scanf("%d", &cnt);
        vector<int> v(cnt);
        for (int i = 0; i < cnt; i++)
		{
            scanf("%d", &v[i]);
            a[v[i]] = 1;
        }
        for (int i = 0; i < v.size(); i++)
            for (int j = 0; j < m[v[i]].size(); j++)
                if (a[m[v[i]][j]] == 1) flag = 1;
        printf("%s\n",flag ? "No" :"Yes");
    }
    return 0;
}

分析:用 map 存儲每一個貨物的所有不兼容貨物(用到了 map 的新用法),在判斷給出的一堆貨物是否是相容的時候,判斷任一貨物的不兼容貨物是否在這堆貨物中,如果存在不兼容的貨物,則這堆貨物不能相容,如果遍歷完所有的貨物,都找不到不兼容的兩個貨物,則這堆貨物就是兼容的


1091 N-自守數

在這裏插入圖片描述

#include <iostream>
#include <string>
using namespace std;
int main()
{
    int m;
    cin >> m;
    while (m--)
	{
        int k, flag = 0;
        cin >> k;
        for (int n = 1; n < 10; n++) //枚舉
		{
            int mul = n * k * k;
            string smul = to_string(mul), sk = to_string(k);
            string smulend = smul.substr(smul.length() - sk.length());
            if (smulend == sk)
			{
                printf("%d %d\n", n, mul);
                flag = 1;
                break;
            }
        }
        if (flag == 0) printf("No\n");
    }
    return 0;
}

題目和代碼簡單,解析一下 string 類的新用法:substr()函數

string temp;
temp = suf.substr(Off, Count)

off 參數: 複製子字符串的起始位置
Count 參數: 複製的字符數目

下面再舉例說明:

string suf = "abcde";
string temp,temp2;
temp = suf.substr(2,1);
temp2 = suf.substr(2); //若沒有第二個參數則代表一直複製到結尾

//下面實例看出是從 0開始計數
output:
c
cde

1093 字符串A+B

在這裏插入圖片描述

#include <iostream>
using namespace std;
int main()
{
    string s1, s2, s;
    int hash[200] = {0};
    getline(cin, s1);
    getline(cin, s2);
    s = s1 + s2;
    for (int i = 0; i < s.size(); i++) //妙招 輸出並剔除重複字符
	{
        if (hash[s[i]] == 0) cout << s[i];
        hash[s[i]] = 1;
    }
    return 0;
}

分析:因爲題目要求若有重複字符先出現 A 的,故做法爲:A + B 在剔除重複字符


1095 解碼PAT准考證

在這裏插入圖片描述
在這裏插入圖片描述

#include <iostream>
#include <vector>
#include <unordered_map> //更快(僅適用於 PTA) 
#include <algorithm>
using namespace std;
struct node
{
    string t; //1類准考證 3類考場號
    int value; //1類成績 3類考場人數 
};
bool cmp(const node &a, const node &b) //用引用傳參 (速度更快 養成好習慣) 
{
    return a.value != b.value ? a.value > b.value : a.t < b.t;
}
int main()
{
    int n, k, num;
    string s;
    cin >> n >> k;
    vector<node> v(n);
    for (int i = 0; i < n; i++)
        cin >> v[i].t >> v[i].value;
    for (int i = 1; i <= k; i++)
	{
        cin >> num >> s;
        printf("Case %d: %d %s\n", i, num, s.c_str());
        vector<node> ans; //存儲篩選後最終數組 
        int cnt = 0, sum = 0;
        if (num == 1) //1類 
		{
            for (int j = 0; j < n; j++)
                if (v[j].t[0] == s[0]) ans.push_back(v[j]); //等級查詢 
        }
		else if (num == 2) //2類 
		{
            for (int j = 0; j < n; j++)
			{
                if (v[j].t.substr(1, 3) == s) //考場查詢 
				{
                    cnt++;
                    sum += v[j].value;
                }
            }
            if (cnt != 0) printf("%d %d\n", cnt, sum);
        }
		else if (num == 3) //3類 
		{
            unordered_map<string, int> m;
            for (int j = 0; j < n; j++)
                if (v[j].t.substr(4, 6) == s) m[v[j].t.substr(1, 3)]++; //考試日期 考場編號人數 ++ 
            for (auto it : m) ans.push_back({it.first, it.second}); //用 it 遍歷 容器 m中的每一個元素 
        }
        sort(ans.begin(), ans.end(),cmp); //一個 cmp 全類型通用 
        for (int j = 0; j < ans.size(); j++)
            printf("%s %d\n", ans[j].t.c_str(), ans[j].value);
        if (((num == 1 || num == 3) && ans.size() == 0) || (num == 2 && cnt == 0)) printf("NA\n");
    }
    return 0;
}

分析非常有參考價值的分類題

題目大意:給出一組學生的准考證號和成績,准考證號包含了等級(乙甲頂),考場號,日期,和個人編號信息,並有三種查詢方式
查詢一:給出考試等級,找出該等級的考生,按照成績降序,准考證升序排序
查詢二:給出考場號,統計該考場的考生數量和總得分
查詢三:給出考試日期,查詢改日期下所有考場的考試人數,按照人數降序,考場號升序排序

代碼:先把所有考生的准考證和分數記錄下來
1、按照等級查詢,枚舉選取匹配的學生,然後排序即可
2、按照考場查詢,枚舉選取匹配的學生,然後計數、求和
3、按日期查詢每個考場人數,用 unordered_map 存儲,最後排序彙總

注意:1、第三個用 map 存儲會超時,用unordered_map就不會超時啦
2、排序傳參建議用引用傳參,這樣更快,雖然有時候不用引用傳參也能通過,但還是儘量用,養成好習慣

解析c_str() 函數的用法

作用:將 C++ 的 string 轉化爲 C 的字符串數組輸出

string temp = "123";
printf("%s",temp.c_str());

//這樣就可以將 temp 以 c語言 的形式輸出(目的是爲了快)
output:123

補充for (auto it : m) 函數的用法

c++11的新特性,範圍 for
m 是一個可遍歷的容器或流,比如 vector 類型
it 就用來在遍歷過程中獲得容器裏的每一個元素

vector<int> m={1,2,3,4};
for(auto it : v)
	cout<<it;

output: 1234
//注意僅 PTA 適用,藍橋杯不允許
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章