今天筆者感覺有點手生了,找了個洛谷入門賽共10道題練練手,題目比較簡單,也有兩個題目比較經典,望讀者不喜勿噴。
【深基附B例】數列求和
題目來源
https://www.luogu.com.cn/problem/P5745
題目描述
給定 n 個正整數組成的數列 a1,a2,⋯ ,an和一個整數 M。要求從這個數列中找到一個子區間 [i,j],也就是在這個數列中連續的數字 ai,ai+1,⋯ ,aj−1,aj使得這個子區間的和在不超過 M的情況下最大。輸出 i、j 和區間和。如果有多個區間符合要求,請輸出 i 最小的那一個。對於所有測試數據,ai≤105, M≤109。
輸入格式
輸入第一行兩個數,代表n和m。 第二行共有n個數,第i個數代表a[i]。
輸出格式
輸出符合題意的區間的左端點、右端點和累加和,中間用空格隔開。
輸入輸出樣例
輸入 #1
5 10
2 3 4 5 6
輸出 #1
1 3 9
說明/提示
子任務 1(10分):n≤200 ;
子任務 2(20分):n≤3000 ;
子任務 3(30分):n≤105;
子任務 4(40分):n≤4×106 。
解題思路
由於n特別大,如果採用最直觀的方法尋找最佳的i,最佳的j,一共兩層for循環,再加上求個sum(i,j),時間複雜度爲O(n3),顯然會超時,這裏想辦法將時間複雜度降爲O(n),通過一次遍歷求出best。
維護一個隊列,讓數組中的元素依次入隊,並記錄這個隊列的元素和,若大於m,則讓對首出列,更新答案,再讓後面的數字繼續入隊,並更新答案,不斷的這麼操作,直到所有數字都入過隊了爲止。
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<queue>
#include<bits/stdc++.h>
using namespace std;
//維護一個隊列,讓數組中的數依次入隊,
//並記錄其的元素和,若大於m,則讓對首出列,更新答案,
//再讓後面的數字繼續入隊,並更新答案,不斷的這麼操作,直到所有數字都入過隊了爲止。
int a[4000001];
//定義一個隊列
queue<int>bestx;
int main()
{
int n;
long long m;
long long best = 0;
long long temp = 0;
cin>>n>>m;
int start,endd,besti,bestj;
//start,endd爲當前維護隊列的首尾元素在數組中的下標位置
//besti,bestj爲當前最優值所對應的數組的起,止元素下標
for(int i = 1;i<=n;i++)
cin>>a[i];
start = endd = 1;
for(int i = 1;i<=n;i++){
//對每一個a[i]都入隊
endd = i;
bestx.push(a[i]);
temp += a[i];
while(temp > m){
bestx.pop();//隊首元素彈出,此時隊首元素爲a[start]
temp = temp - a[start];
start++;//隊列中的第一個下標爲start
}
//判斷更新最優值
if(best < temp){
besti = start;
bestj = endd;
best = temp;
}
}//for
cout<<besti<<" "<<bestj<<" "<<best;
return 0;
}
【深基2.例5】蘋果採購
題目來源
https://www.luogu.com.cn/problem/P5703
題目描述
現在需要採購一些蘋果,每名同學都可以分到固定數量的蘋果,並且已經知道了同學的數量,請問需要採購多少個蘋果?
輸入格式
輸入兩個不超過 109 正整數,分別表示每人分到的數量和同學的人數。
輸出格式
一個整數,表示答案。保證輸入和答案都在int範圍內的非負整數。
輸入輸出樣例
輸入 #1
5 3
輸出 #1
15
解題思路
沒有什麼解題思路,題目都說保證輸入和答案都在int可表示的範圍內了,直接cout<<x*y;
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
int main()
{
int x,y;
cin>>x>>y;
cout<<x*y;
return 0;
}
【深基2.習6】Apples Prologue
題目來源
https://www.luogu.com.cn/problem/P5709
題目描述
八尾勇喜歡吃蘋果。她現在有 m(m≤100) 個蘋果,吃完一個蘋果需要花費 t(t≤100)分鐘,吃完一個後立刻開始吃下一個。現在時間過去了 s(s≤10000) 分鐘,請問她還有幾個完整的蘋果?
輸入格式
輸入三個非負整數表示 m 、t 和 s。
輸出格式
輸出一個整數表示答案。
輸入輸出樣例
輸入 #1
50 10 200
輸出 #1
30
解題思路
這個題看起來是不是超級簡單,但是不可能會這麼容易過的,要注意跳坑。筆者就掉進去了一次,而且這題目在特定情況下也有毛病。
- 當 t != 0的時候,當s/t整除(即s%t == 0)時,輸出m-s/t;當s/t不整除(即s%t != 0)時,輸出m-s/t-1.
- 你以爲上面是對的嗎?不對,如果m-s/t 或者 m-s/t-1 小於0怎麼辦?顯然剩餘的完整蘋果數一定是一個非負整數,當此類情況出現時,要輸出0.
- 當t == 0時,這就是爭議的地方,意思是八尾勇一瞬間可以吃完一個蘋果,所以按照常理,那麼剩餘的完整蘋果數應該爲0呀!筆者這麼做只能得90分,看了題解討論之後發現這種情況輸出m纔可以AC通過,這就是筆者認爲不合理得地方。
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
int main()
{
int m,t,s;
cin>>m>>t>>s;
//如果吃一個蘋果爲0分鐘
if(t == 0){
cout<<m;//題目有毛病,特判
return 0;
}
int p;
p = s/t;
if(s%t != 0)//特判
p++;
m = m - p;
//如果蘋果不夠吃
if(m <= 0)
cout<<0;//則剩餘完整的蘋果數爲0
else
cout<<m;
return 0;
}
【深基3.例8】三位數排序
題目來源
https://www.luogu.com.cn/problem/P5715
題目描述
給出三個整數 a,b,c(0≤a,b,c≤100),要求把這三位整數從小到大排序。
輸入格式
無
輸出格式
無
輸入輸出樣例
輸入 #1
1 14 5
輸出 #1
1 5 14
輸入 #2
2 2 2
輸出 #2
2 2 2
解題思路
直接冒泡排序了,反正3個數時間複雜度也不會高,應該能AC。
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
//給出三個整數 a,b,c(0≤a,b,c≤100)a,b,c
//a,b,c(0≤a,b,c≤100),要求把這三位整數從小到大排序。
int a[4];
int main()
{
for(int i=1;i<=3;i++)
cin>>a[i];
//冒泡排序
for(int i=1;i<=3;i++)
for(int j=1;j<=3-i;j++)
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
//遍歷輸出
for(int i=1;i<=3;i++)
cout<<a[i]<<" ";
return 0;
}
【深基4.例6】數字直角三角形
題目來源
https://www.luogu.com.cn/problem/P5721
題目描述
給出n(1≤n≤13),請輸出一個直角邊長度是 n 的數字直角三角形。所有數字都是 2 位組成的,如果沒有 2 位則加上前導 0。
輸入格式
無
輸出格式
無
輸入輸出樣例
輸入 #1
5
輸出 #1
0102030405
06070809
101112
1314
15
解題思路
計算了一下,第一層n個數,第n層1個數,一共(n+1)*n/2個數,n最大13,最多有91個數,都是兩位數,不存在三位數或者更高位得數,只有前導0得問題。
水題,兩層for循環遍歷
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
//給出n(1≤n≤13)
//請輸出一個直角邊長度是 nnn 的數字直角三角形。
//所有數字都是 2 位組成的,如果沒有 2 位則加上前導 0。
int main()
{
int n;//
cin>>n;
int cnt = 1;
for(int i = 1;i <= n; i++){
for(int j = 1; j <= n+1-i; j++){
if(cnt < 10){//補上前導0
cout<<0<<cnt;
}
else
cout<<cnt;
cnt++;
}
cout<<endl;//每行換行
}
return 0;
}
【深基5.例3】冰雹猜想
題目來源
https://www.luogu.com.cn/problem/P5727
題目描述
給出一個正整數 n(n≤100),然後對這個數字一直進行下面的操作:如果這個數字是奇數,那麼將其乘 3 再加 1,否則除以 2。經過若干次循環後,最終都會回到 1。經過驗證很大的數字(7×1011)都可以按照這樣的方式比變成 1,所以被稱爲“冰雹猜想”。例如當 n是 20,變化的過程是 [20, 10, 5, 16, 8, 4, 2, 1]。
根據給定的數字,驗證這個猜想,並從最後的 1 開始,倒序輸出整個變化序列。
輸入格式
無
輸出格式
無
輸入輸出樣例
輸入 #1
20
輸出 #1
1 2 4 8 16 5 10 20
解題思路
可以遞歸求解,我想法比較直觀,求出每一次的變換的數,直到最後爲1,將這些數全部裝到數組裏,然後逆序輸出就行。
爲防止中間數據太長,數組長度開到104。
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
int a[10000];
int hanshu(int m)
{
if (m == 1)
return 1;
else if(m%2 == 0)
return m/2;
else
return 3*m + 1;
}
int main()
{
int n,x;//n<=1000
cin>>n;
int i = 1;
a[1] = n;
while(1){
if(a[i] == 1)
break;
a[i+1] = hanshu(a[i]);
i++;
}
for(int t = i;t >= 1;t--)
cout<<a[t]<<" ";
return 0;
}
【深基6.例1】自動修正
題目來源
https://www.luogu.com.cn/problem/P5733
題目描述
大家都知道一些辦公軟件有自動將字母轉換爲大寫的功能。輸入一個長度不超過 100 且不包括空格的字符串。要求將該字符串中的所有小寫字母變成大寫字母並輸出。
輸入格式
無
輸出格式
無
輸入輸出樣例
輸入 #1
Luogu4!
輸出 #1
LUOGU4!
解題思路
啥也不多說了,水題。
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
int main()
{
string str;
cin>>str;
int len = str.length();
for(int i=0;i<len;i++){
if(str[i]>='a' && str[i]<='z')
str[i] = (char)(((str[i] - 'a')+ 26)%26+'A');
}
cout<<str;
return 0;
}
【深基7.例7】計算階乘
題目來源
https://www.luogu.com.cn/problem/P5739
題目描述
求 n!(n≤12),也就是 1×2×3…×n。
挑戰:嘗試不使用循環語句(for、while)完成這個任務。
輸入格式
無
輸出格式
無
輸入輸出樣例
輸入 #1
3
輸出 #1
6
解題思路
遞歸函數,最低級的遞歸。
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
//計算階乘
long long fact(int n)
{
if(n==0 || n==1)
return 1;
else
return n*fact(n-1);
}
int main()
{
int n;
cin>>n;
cout<<fact(n);
return 0;
}
【深基15.例2】寄包櫃
題目來源
https://www.luogu.com.cn/problem/P3613
題目描述
超市裏有 n(n≤105)個寄包櫃。每個寄包櫃格子數量不一,第 i 個寄包櫃有 ai(ai≤105)個格子,不過我們並不知道各個 ai 的值。對於每個寄包櫃,格子編號從 1 開始,一直到 ai。現在有 q(q≤105) 次操作:
1 i j k:在第 i個櫃子的第 j個格子存入物品 k(0≤k≤109)。當 k=0時說明清空該格子。
2 i j:查詢第 i個櫃子的第 j個格子中的物品是什麼,保證查詢的櫃子有存過東西。
已知超市裏共計不會超過 107 個寄包格子,ai是確定然而未知的,但是保證一定不小於該櫃子存物品請求的格子編號的最大值。當然也有可能某些寄包櫃中一個格子都沒有。
輸入格式
第一行 2 個整數 n 和 q,寄包櫃個數和詢問次數。
接下來 q 行整數,每行表示一次操作。
輸出格式
對於查詢操作時,輸出答案。
輸入輸出樣例
輸入 #1
5 4
1 3 10000 114514
1 1 1 1
2 3 10000
2 1 1
輸出 #1
114514
1
解題思路
因爲保證每次查詢的數都是可以查詢的,那麼可以爲每一個格子定義一個結構體
typedef struct
{
int x;
int y;
int value;
}fangge;
但是每次查詢時都得在這個結構體數組中進行查詢,當輸入的兩個量分別和x,y相等時纔是所要的數據,這個時間複雜度爲O(n),由於n≤105,ai≤105,很顯然應該會超時。
筆者試過,80分代碼如下:
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
typedef struct
{
int x;
int y;
int value;
}shopping;
int main()
{
int n,q;
cin>>n>>q;
shopping a[q+1];
int key[q+1];//存放每次查詢結果,方便一併輸出
int opt,x,y,z;
int t = 1,sqm = 1;
for(int i = 1;i<=q;i++){
cin>>opt;//輸入操作碼
if(opt == 1){ //新節點
cin>>x>>y>>z;
a[t].x = x;
a[t].y = y;
a[t].value = z;
t++;
}
else if(opt == 2){
cin>>x>>y; //查詢
for(int j = 1;j<=t;j++)
if(a[j].x == x && a[j].y == y)
key[sqm] = a[j].value;
sqm++;
}
}
//輸出查詢結果
//查詢結果數爲 sqm - 1
for(int i = 1;i<sqm;i++)
cout<<key[i]<<endl;
return 0;
}
我們可以通過直接索引的方式將每次查找的時間複雜度降爲O(1)。
用到STL中的map。
#include<bits/stdc++.h>
map<int int>a1[100001];
AC代碼如下:
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<bits/stdc++.h>
using namespace std;
map<int,int>a1[100001];
int main()
{
int n,q;
cin>>n>>q;
int key[q+1];
int opt,x,y,z;//每一行先輸入一個數,是1表示裝入,是2表示查詢
int sqm = 1;
for(int i = 1;i<=q;i++){
cin>>opt;
if(opt == 1){
cin>>x>>y>>z;
a1[x][y] = z;
}
else if(opt == 2){
cin>>x>>y;
//索引查找
key[sqm] = a1[x][y];
sqm++;
}
}
//輸出查詢結果
//查詢結果總數爲 sqm - 1
for(int i = 1;i<sqm;i++)
cout<<key[i]<<endl;
return 0;
}
【深基17.例6】學籍管理
題目來源
https://www.luogu.com.cn/problem/P5266
題目描述
您要設計一個學籍管理系統,最開始學籍數據是空的,然後該系統能夠支持下面的操作(不超過 100000條):
插入與修改,格式1 NAME SCORE:在系統中插入姓名爲 NAME(由字母和數字組成不超過 20 個字符的字符串,區分大小寫) ,分數爲 SCORE(0<SCORE<10000) 的學生。如果已經有同名的學生則更新這名學生的成績爲 SCORE。如果成功插入或者修改則輸出OK。
查詢,格式2 NAME:在系統中查詢姓名爲 NAME 的學生的成績。如果沒能找到這名學生則輸出Not found,否則輸出該生成績。
刪除,格式3 NAME:在系統中刪除姓名爲 NAME 的學生信息。如果沒能找到這名學生則輸出Not found,否則輸出Deleted successfully。
彙總,格式4:輸出系統中學生數量。
輸入格式
無
輸出格式
無
輸入輸出樣例
輸入 #1
5
1 lxl 10
2 lxl
3 lxl
2 lxl
4
輸出 #1
OK
10
Deleted successfully
Not found
0
解題思路
還是運用STL中的map,因爲學生的成績和學生的姓名是一個從字符串到整型數據的一種映射關係,而map正好能夠解決這種問題。
同時,題目中的幾種操作,查詢,刪除,更新和彙總,正是map最基本的成員函數的運用。
#include<bits/stdc++.h>
map <string ,int> a; //定義map
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<bits/stdc++.h>
using namespace std;
map <string ,int> a; //定義map
int main()
{
int n;
cin>>n;
int opt;//操作碼
string name;
int score;
for(int i = 1;i <= n;i++){
cin>>opt;
//插入和修改
if(opt == 1){
cin>>name>>score;
a[name] = score;
cout<<"OK"<<endl;
}
//查詢
else if(opt == 2){
cin>>name;
//該生在系統中,可查詢
if(a.count(name) == 1)
cout<<a[name]<<endl;
else
cout<<"Not found"<<endl;
}
//刪除
else if(opt == 3){
cin>>name;
//該生在系統中,可刪除
if(a.count(name) == 1){
a.erase(name);
cout<<"Deleted successfully"<<endl;
}
else
cout<<"Not found"<<endl;
}
//彙總
else if(opt == 4){
cout<<a.size()<<endl;
}
}//for
return 0;
}
總結
C/C++入門級訓練,筆者今天做了之後覺得還比較簡單。