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;
}
先講解 sscanf 和 sprintf 的作用:
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 適用,藍橋杯不允許