2023第十四屆藍橋杯大賽軟件賽省賽C/C++ 大學 B 組(真題題解)(C++/Java題解)

記錄刷題的過程、感悟、題解。
希望能幫到,那些與我一同前行的,來自遠方的朋友😉


大綱:

?1、日期統計-(解析)-暴力dfs(😉藍橋專屬

?2、01串的熵-(解析)-不要chu,認真讀題,并且知道log()怎么用就OK

?3、冶煉金屬-(解析)-其實推理極限,用數學知識就能OK😊

?4、飛機降落-(解析)-暴力搜索dfs(😉藍橋專屬

?5、接龍數列-(解析)-字典dp(😎就是名字高大上點,只是一道dp

?6、島嶼個數-(解析)-bfs+dfs,重點在于會染色+會讀題(廣搜深搜一起整

?7、子串簡寫-(解析)-一道簡單的前綴和

?8、整數刪除-(解析)-優先隊列+雙向鏈表(😎模擬)

9、10 是lca類型的題目,這類題型,攢著。到時候出個專題,一塊搞。😎


知識點:

1、二分查找

2、emplace

3、log() ::cmath::


1、日期統計

問題描述

小藍現在有一個長度為 100?的數組,數組中的每個元素的值都在?0?到?9?的范圍之內。數組中的元素從左至右如下所示:

5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2
7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 3 8 5 1 6 3 4 6 7 0 7 8 2 7 6 8 9 5 6 5 6 1 4 0 1
0 0 9 4 8 0 9 1 2 8 5 0 2 5 3 3

現在他想要從這個數組中尋找一些滿足以下條件的子序列:

  1. 子序列的長度為?8;
  2. 這個子序列可以按照下標順序組成一個?yyyymmdd 格式的日期,并且要求這個日期是?2023 年中的某一天的日期,例如?20230902,20231223。yyyy 表示年份,mm 表示月份,dd?表示天數,當月份或者天數的長度只有一位時需要一個前導零補充。

請你幫小藍計算下按上述條件一共能找到多少個不同的?2023 年的日期。對于相同的日期你只需要統計一次即可。

答案提交

這是一道結果填空的題,你只需要算出結果后提交即可。本題的結果為一個整數,在提交答案時只填寫這個整數,填寫多余的內容將無法得分。

/*
? 其實本題非常簡單,因為它體現了藍橋杯可以 暴力(dfs)的點:
? 首先,以2023開頭是固定的,你可以在前方,先把這個給枝剪掉
? 然后再剩下mmdd這四位中爆搜(dfs,當然多層for循環也行)就行,把所有可能給列舉出來
? 切記,一定要防止日期重復,這點很細節。
*/

C++題解
#include <iostream>
#include <vector>
using namespace std;vector<int> vec{ // 年份剔除,已經存儲完畢3,8, 5 ,1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6, 1, 4, 0, 1,0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3
};vector<vector<bool>> ymd(13,vector<bool>(32,false)); // 已經非常確定了void t(int mm, int dd){ // 檢測if(mm==1||mm==3||mm==5||mm==7||mm==8||mm==10||mm==12){ // 31天if(0<dd&&dd<=31) ymd[mm][dd]=true;}else if(mm==2){ // 28天if(0<dd&&dd<=28) ymd[mm][dd]=true;}else if(mm==4||mm==6||mm==9||mm==11){ // 30天if(0<dd&dd<=30) ymd[mm][dd]=true;}
}void dfs(int cur, int pla, string str){if(cur==4){ // 判斷int mm = stoi(str.substr(0,2));int dd = stoi(str.substr(2,2));t(mm,dd);return;}for(int i=pla+1; i<vec.size(); ++i){dfs(cur+1,i,str+ to_string(vec[i]));}
}int main()
{dfs(0,0,"");int sum = 0;for(int i=0; i<ymd.size(); ++i){for(int j=0; j<32; ++j){if(ymd[i][j]) sum++;}}cout<<sum<<endl;return 0;
}
Java題解
import java.util.ArrayList;
import java.util.List;public class Main {// 年份剔除,已經存儲完畢static List<Integer> vec = List.of(3, 8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6, 1, 4, 0, 1,0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3);// 已經非常確定了static boolean[][] ymd = new boolean[13][32];// 檢查日期是否合法并標記static void t(int mm, int dd) {if (mm == 1 || mm == 3 || mm == 5 || mm == 7 || mm == 8 || mm == 10 || mm == 12) {// 31 天的月份if (0 < dd && dd <= 31) {ymd[mm][dd] = true;}} else if (mm == 2) {// 28 天的月份if (0 < dd && dd <= 28) {ymd[mm][dd] = true;}} else if (mm == 4 || mm == 6 || mm == 9 || mm == 11) {// 30 天的月份if (0 < dd && dd <= 30) {ymd[mm][dd] = true;}}}// 深度優先搜索static void dfs(int cur, int pla, String str) {if (cur == 4) {// 判斷int mm = Integer.parseInt(str.substring(0, 2));int dd = Integer.parseInt(str.substring(2, 4));t(mm, dd);return;}for (int i = pla + 1; i < vec.size(); ++i) {dfs(cur + 1, i, str + vec.get(i));}}public static void main(String[] args) {dfs(0, 0, "");int sum = 0;for (int i = 0; i < ymd.length; ++i) {for (int j = 0; j < 32; ++j) {if (ymd[i][j]) {sum++;}}}System.out.println(sum);}
}

2、01串的熵

// 只要你不chu,認真讀題,仔細觀察,還是能做本題的

/*
? 但是前提條件之一是,你要知道log的用法 =底數;<cmath>
? C++中,log默認是 ln(..) ,要做到log(真數,指數的底數),這一步,
需要用換底公式:=ln(真數)/ln(指數的底數)
? 這涉及到,數學中的指數式 與 對數的轉換
? 細節:log(float/double) 內部只能傳遞這兩種類型,傳出也是,否則也會被隱式轉換
*/
/*
? 本題的式子,都給的這么明顯了,怎么可能還搞不出來?
? H(S)= 首先是負號、然后觀察 100,一個1,兩個0。看出什么沒?
? 當然是后面的對應順序呀。共3個(1個1 1/3) (2個0 2/3)還有個數。
?“ 0 出現次數比 1 少 ”
?根據題意:所以0的個數一定1~23333333/2,之間的某個數字。
*/

C++版
#include <iostream>
#include <cmath>
using namespace std;int main()
{int N = 23333333;for(int i=1; i<=N/2; ++i){int n0 = i; // 0的個數;int n1 = N-i; // 1的個數;double sum = 0;sum-=(double)n0/N*log((double)n0/N)/ log(2)*n0;sum-=(double)n1/N*log((double)n1/N)/ log(2)*n1;if(fabs(sum-11625907.5798)<1e-4){cout<<n0<<endl;break;}}return 0;
}
Java版
import java.lang.Math;public class Main {public static void main(String[] args) {int N = 23333333;for (int i = 1; i <= N / 2; ++i) {int n0 = i; // 0的個數int n1 = N - i; // 1的個數double sum = 0;sum -= (double) n0 / N * Math.log((double) n0 / N) / Math.log(2) * n0;sum -= (double) n1 / N * Math.log((double) n1 / N) / Math.log(2) * n1;if (Math.abs(sum - 11625907.5798) < 1e-4) {System.out.println(n0);break;}}}
}

3、冶煉金屬

問題描述

小藍有一個神奇的爐子用于將普通金屬?O?冶煉成為一種特殊金屬?X。這個爐子有一個稱作轉換率的屬性?V,V?是一個正整數,這意味著消耗?V?個普通金屬?O?恰好可以冶煉出一個特殊金屬?X,當普通金屬?O?的數目不足?V?時,無法繼續冶煉。

現在給出了?N?條冶煉記錄,每條記錄中包含兩個整數?A?和?B,這表示本次投入了?A?個普通金屬?O,最終冶煉出了?B?個特殊金屬?X。每條記錄都是獨立的,這意味著上一次沒消耗完的普通金屬?O?不會累加到下一次的冶煉當中。

根據這?N?條冶煉記錄,請你推測出轉換率?V 的最小值和最大值分別可能是多少,題目保證評測數據不存在無解的情況。

輸入格式

第一行一個整數?N,表示冶煉記錄的數目。

接下來輸入?N?行,每行兩個整數?A、B,含義如題目所述。

輸出格式

輸出兩個整數,分別表示?V?可能的最小值和最大值,中間用空格分開。

樣例輸入

3
75 3
53 2
59 2

樣例輸出

20 25

樣例說明

當?V=20 時,有:?75/20?=3,?53/20?=2,?59/20?=2,可以看到符合所有冶煉記錄。

當?V=25時,有:?75/25?=3,?53/25?=2,?59/25?=2,可以看到符合所有冶煉記錄。

且再也找不到比?20?更小或者比?25 更大的符合條件的?V?值了。

評測用例規模與約定

對于?30% 的評測用例,1≤N≤10^2。

對于?60% 的評測用例,1≤N≤10^3。

對于?100% 的評測用例,1≤N≤10^4,1≤B≤A≤10^9。

// 本題為貪心算法-從局部推至整體、
// 我看網上,其他人用了很多復雜的方法,什么差分吶...
// 好像用不到耶,只需要,推逼一下極限就行
/*
? 聽好嘍,這幾部很關鍵。
? 什么是轉化率消耗的最小值? 極限就是你剛好能轉化 B+1塊,
? ? 但因為是推理出來的極限,實際上是不存在的,也就是差一點點。所以要再結果上在+1塊
(例:75/4=18,18+1=19,19就是此時的極限)
? 同樣什么是轉化率消耗的最大值?你的所有金屬A,剛好轉哈為B塊。A/B
*/
// 希望對你有幫助

C++版
#include <iostream>
#define ll long long
using namespace std;
const ll maxl = 0x3f3f3f3f3f3f3f3f; // 偽最大值int main()
{int t;cin>>t;int maxV=maxl,minV=0; // 涉及一個極端情況。int A,B;while(t--){cin>>A>>B;minV=max((A/(B+1)+1),minV); // 最小值maxV=min((A/B),maxV);   // 最大值}cout<<minV<<" "<<maxV;return 0;
}
Java版
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);long maxV = Long.MAX_VALUE; // 初始化為最大可能值long minV = 0; // 初始化為最小可能值int t = scanner.nextInt();for (int i = 0; i < t; i++) {long A = scanner.nextLong();long B = scanner.nextLong();// 計算當前測試用例的最小和最大值long currentMin = (A / (B + 1)) + 1;long currentMax = A / B;// 更新全局的最小和最大值if (currentMin > minV) {minV = currentMin;}if (currentMax < maxV) {maxV = currentMax;}}System.out.println(minV + " " + maxV);}
}

4、飛機降落

問題描述

N?架飛機準備降落到某個只有一條跑道的機場。其中第?i?架飛機在?Ti??時刻到達機場上空,到達時它的剩余油料還可以繼續盤旋?Di 個單位時間,即它最早可以于?Ti??時刻開始降落,最晚可以于?Ti+Di??時刻開始降落。降落過程需要?Li??個單位時間。

一架飛機降落完畢時,另一架飛機可以立即在同一時刻開始降落,但是不能在前一架飛機完成降落前開始降落。

請你判斷?N?架飛機是否可以全部安全降落。

輸入格式

輸入包含多組數據。

第一行包含一個整數?T,代表測試數據的組數。

對于每組數據,第一行包含一個整數?N。

以下?N?行,每行包含三個整數:Ti?,Di??和?Li?。

輸出格式

對于每組數據,輸出?YES?或者?NO,代表是否可以全部安全降落。

樣例輸入

2
3
0 100 10
10 10 10
0 2 20
3
0 10 20
10 10 20
20 10 20

樣例輸出

YES
NO

樣例說明

對于第一組數據,可以安排第?3?架飛機于?0?時刻開始降落,20?時刻完成降落。安排第?2?架飛機于?20?時刻開始降落,30?時刻完成降落。安排第?1?架飛機于?30?時刻開始降落,40?時刻完成降落。

對于第二組數據,無論如何安排,都會有飛機不能及時降落。

評測用例規模與約定

對于?30% 的數據N≤2。

對于?100% 的數據 1≤T≤10,1≤N≤10,0≤Ti,Di,Li≤10^5。

// 我當時寫的時候,一直輪詢檢測超時。我還以為是太暴力了(┬┬﹏┬┬),結果...是藍橋杯官網出問題了,提交不了,可惡。
// 人家都給你飛機了,而且最多不會超過10架
// 題都給到這種程度了,還猶豫什么???直接爆搜,把所有情況給枚舉出來。(最多10!)
// 所以說好聽點,本題本質上就是一道組合題目
/*
? 你要先創建一個數組used[N],這臺飛機你遍歷過沒
? 然后在爆搜期間,維持一個cnt(原count、簡寫),去記錄到底停了幾架飛機
? ---以上,這些只是方便縷清思路的一環,接下來才是關鍵---
? 咱假設cur_start是你的最晚開始落下時間、cur_last 其他飛機能開始落下的時間。
? cur_start=Ti+Di 是你的最晚開始落下時間、
? 而cur_last=Ti+Di+Li 才是你最晚落下后,并且能讓其他飛機降落的時間。
? 也就是 cur_last = cur_start+Li;
? 但是認真讀過題的都知道!
? 開始降落的時間,不一定非要是Ti+Di,
? 他取決于2點:

  • 上一架飛機,是啥時候落下的(other_last)
  • 能不能在Ti(最早)時刻落下(Ti)。

? 此刻if(other_last>Ti) cur_last = other_last;
? ? ? if(other_last<Ti) cur_last = Ti;
(確切的是,畫個圖就知道了)
? OK,知道這些了,你確定你還會做不出來嗎?
? 爆搜開始
*/

C++版
#include <iostream>
#include <vector>
using namespace std;const int N = 1e1+5;
int t,n;
vector<bool> used(N,false); // 是否已經降落
vector<vector<int>> res(N,vector<int>(3,0));int flag = false;void dfs(int cur ,int tim){ // nif(cur==n){ // 成功的條件flag=true;return;}for(int i=0; i<n; ++i){// 標準回溯if(!used[i] && tim<=(res[i][0]+res[i][1])){ // tim>vec[i][1] 超過了最晚截止時間used[i]=true;// cout<<i<<" "<<used[i]<<" "<<tim<<" "<<res[i][0]<<" "<<res[i][1]<<endl;int cur_start = max(res[i][0],tim);dfs(cur+1,cur_start+res[i][2]);used[i]=false;}}}int main()
{cin>>t;while(t--){for(int i=0; i<n; ++i) used[i]=false;cin>>n;for(int i=0; i<n; ++i){cin>>res[i][0]>>res[i][1]>>res[i][2]; // 存}dfs(0,0);if(flag) cout<<"YES"<<endl;else cout<<"NO"<<endl;flag = false;}return 0;
}
Java版
import java.util.Scanner;
import java.util.ArrayList;public class Main {static final int N = 105;static int t, n;static boolean[] used = new boolean[N];static int[][] res = new int[N][3];static boolean flag = false;public static void main(String[] args) {Scanner scanner = new Scanner(System.in);t = scanner.nextInt();while (t-- > 0) {for (int i = 0; i < N; i++) {used[i] = false;}n = scanner.nextInt();for (int i = 0; i < n; i++) {res[i][0] = scanner.nextInt();res[i][1] = scanner.nextInt();res[i][2] = scanner.nextInt();}flag = false;dfs(0, 0);if (flag) {System.out.println("YES");} else {System.out.println("NO");}}scanner.close();}static void dfs(int cur, int tim) {if (cur == n) {flag = true;return;}for (int i = 0; i < n; i++) {if (!used[i] && tim <= (res[i][0] + res[i][1])) {used[i] = true;int cur_start = Math.max(res[i][0], tim);dfs(cur + 1, cur_start + res[i][2]);used[i] = false;}}}
}

5、接龍數列

問題描述

對于一個長度為?K?的整數數列:A1,A2,…,AK,我們稱之為接龍數列當且僅當?Ai??的首位數字恰好等于?Ai?1??的末位數字?(2≤i≤K)。例如?12,23,35,56,61,11是接龍數列;12,23,34,56 不是接龍數列,因為?56?的首位數字不等于?34?的末位數字。所有長度為?1?的整數數列都是接龍數列。

現在給定一個長度為?N?的數列?A1,A2,…,AN,請你計算最少從中刪除多少個數,可以使剩下的序列是接龍序列?

輸入格式

第一行包含一個整數?N。

第二行包含?N?個整數?A1,A2,…,AN?。

輸出格式

一個整數代表答案。

樣例輸入

5
11 121 22 12 2023

樣例輸出

1

樣例說明

刪除?22,剩余?11,121,12,2023 是接龍數列。

評測用例規模與約定

對于?20% 的數據,1≤N≤20。

對于?50% 的數據,1≤N≤10000。

對于?100% 的數據,1≤N≤105,1≤Ai≤109。所有?Ai?保證不包含前導?0。

// 有很多,特殊的案例的,11 12 13 35
// 額,字典dp這玩意... 我真的還是第一次聽說耶,
// 挺值得思考的,為什么我在第一次做本題的時候,咋就沒有往動態規劃上想
// 他們都是這樣解釋字典dp的:利用(哈希表)來儲存和管理狀態的優化方式
// 簡而言之,就是通過,鍵值對儲存最優解,適用于不連續的場景,或范圍較大的場景
/*
? 好啦、知道啥是字典序了,就來解決一下本題 -->
? 肯定很多人,在一開始,直接想到貪心、局部最優 推導 全局最優。
? 于是就嚇唬亂刪一通。
? 舉個例子: 11 12 13 35;
? 你能亂刪嗎?
? 刪 13、35 -> 你就會得到 11 12
? 刪 12 ? ? -> 你就會得到 11 13 15
? 那?該怎么做?難道是將所有的可能,都給枚舉出來刪一遍?
? 額、包超時的。
? 這個時候,你仔細觀察就會發現一個很有趣的現象。
? 畢竟題目就叫做接龍了。
? 不論你刪誰,都跟你的上一個以本首字母,為結尾的字母的狀態有關。
? 這不就是 動態規劃,能推導的前提嗎?
? 原順序 13 35 34 45 56
? ? ? ? ? ? ? ? ? ? ? ? ? ?__34-45
? 舉個列子:13-? ? ? ? ? ? ? ?-56 (56你接在誰后面?
? ? ? ? ? ? ? ? ? ? ? ? ? ?--35
? 這個時候,35經過判斷,會接在56后邊
? 所以,這個時候維護一個dp[0~9]之后,dp[i]的含義就是,以i為起始位置的串的長度
? 遞歸公式 dp[last]=max(dp[start]+1,dp[last]);
*/

C++版
#include <iostream>
using namespace std;int main()
{int dp[10]={0};int n; cin>>n;for(int i=0; i<n; ++i){string str;cin>>str;int i1 = str[0]-'0';int i2 = str[str.size()-1]-'0';dp[i2] = max(dp[i1]+1,dp[i2]);}int maxn=0;for(int i=0; i<10; ++i) maxn=max(maxn,dp[i]);cout<<n-maxn<<endl;return 0;
}
Java版
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int[] dp = new int[10]; // 初始化動態規劃數組int n = scanner.nextInt();for (int i = 0; i < n; i++) {String str = scanner.next();int i1 = str.charAt(0) - '0'; // 首字符轉數字int i2 = str.charAt(str.length() - 1) - '0'; // 尾字符轉數字// 更新動態規劃狀態if (dp[i1] + 1 > dp[i2]) {dp[i2] = dp[i1] + 1;}}int maxn = 0;for (int num : dp) {if (num > maxn) {maxn = num;}}System.out.println(n - maxn);}
}

6、島嶼個數

問題描述

小藍得到了一副大小為?M×N?的格子地圖,可以將其視作一個只包含字符 '0'(代表海水)和 '1'(代表陸地)的二維數組,地圖之外可以視作全部是海水,每個島嶼由在上/下/左/右四個方向上相鄰的 '1' 相連接而形成。

在島嶼?A?所占據的格子中,如果可以從中選出?kk?個不同的格子,使得他們的坐標能夠組成一個這樣的排列:(x0,y0),(x1,y1),…,(xk?1,yk?1),其中?(xi+1modk,yi+1modk)是由?(xi,yi)( 通過上/下/左/右移動一次得來的?(0≤i≤k?1),此時這?k?個格子就構成了一個“環”。如果另一個島嶼?B?所占據的格子全部位于這個“環”內部,此時我們將島嶼?B?視作是島嶼?A?的子島嶼。若?B?是?A?的子島嶼,C?又是?B?的子島嶼,那?C?也是?A?的子島嶼。

請問這個地圖上共有多少個島嶼?在進行統計時不需要統計子島嶼的數目。

輸入格式

第一行一個整數?T,表示有?T?組測試數據。

接下來輸入?T?組數據。對于每組數據,第一行包含兩個用空格分隔的整數?M、N?表示地圖大小;接下來輸入?M?行,每行包含?N?個字符,字符只可能是 '0' 或 '1'。

輸出格式

對于每組數據,輸出一行,包含一個整數表示答案。

樣例輸入

2
5 5
01111
11001
10101
10001
11111
5 6
111111
100001
010101
100001
111111

樣例輸出

1
3

樣例說明

對于第一組數據,包含兩個島嶼,下面用不同的數字進行了區分:

01111
11001
10201
10001
11111

島嶼?2?在島嶼?1?的“環”內部,所以島嶼?2?是島嶼?1?的子島嶼,答案為?1。

對于第二組數據,包含三個島嶼,下面用不同的數字進行了區分:

111111
100001
020301
100001
111111

注意島嶼?3?并不是島嶼?1?或者島嶼?2?的子島嶼,因為島嶼?1?和島嶼?2?中均沒有“環”。

評測用例規模與約定

對于?30?的評測用例,1≤M,N≤10。

對于?100?的評測用例,1≤T≤10,1≤M,N≤50。
// 注:在上網了解一下,emplace的作用。
// 搜索一下,fill的作用


// 可惡啊,預處理數組大小開小了。(┬┬﹏┬┬)
// 本題是一道典型的搜索類型題目,不論你是用dfs、還是用bfs,還是倆個結合著用,不論如何這都是非常酷的。
// 我非常確定,肯定有些人,一上來就直接被(x0,y0) (x1,y1)...(xi+1modk,yi+1modk) 給整成小迷糊
// 這樣會導致失去很多細節
// 仔細審題,最好在紙上,畫出來
// 就像本題、通過 上/下/左/右 移動得來的,才算圍成環,畫圖時,就會發現左上呢?右下呢?...等等等
/*
? 作為一道經典的板子題(套路題)
? 做這類型的題目,一般就是現在外層圍一圈海
? 簡而言之,就是在最外圍,多鋪墊一層'0';
? 本題最大的左右就是,在左上角,(0,0)的位置,首先將所有外海染色,
? 為啥(⊙_⊙)?要染色呢,因為沒有染色的,必然有兩種情況
? 1、是島嶼
? 2、是內海
? 這時,思路就清晰了,只要島嶼在內海中,他就是子島嶼,不用計數,但是也要染色,方便繼續搜索。
*/
// 拓展一下,就是emplace家族的用法。(emplace_back、emplace)
// 優點是能直接在容器內,構建元素,避免不必要的拷貝,提高性能
// vector用emplace_back(); list、map等,可用emplace
// 使用方法與insert、push類似。不過是傳入構造元素所需參數。所需參數。

C++版
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e2+5;
int T,m,n; // 組 行 列
vector<vector<int>> arr(N,vector<int>(N,0));
struct node{int x; // 行int y; // 列node(int cx,int cy):x(cx),y(cy){}
};
// 一共8個方向
int fa1[]={0,0,1,-1,1,1,-1,-1};
int fa2[]={1,-1,0,0,-1,1,-1,1};
queue<node> q;
void bfs(){ // 廣搜,用于起始時將外海染色,這個用深搜不太行,因為沒法地毯式染色while(!q.empty()){int x = q.front().x, y = q.front().y;q.pop();if(arr[x][y]!=0) continue; // 不是外海水if(x<0||x>m+1||y<0||y>n+1) continue; // 越界arr[x][y]=2;for(int i=0; i<8; ++i){ // 標準格式,應該不是這樣的if(x+fa1[i]<0||x+fa1[i]>m+1||y+fa2[i]<0||y+fa2[i]>n+1) continue; // 越界q.emplace(x+fa1[i], y+fa2[i]);}}
}
void dfs(int x, int y){ // 深搜,用來將島嶼染色if(arr[x][y]!=1) return; // 不是島嶼if(x<0||x>m+1||y<0||y>n+1) return; // 越界arr[x][y]=3;for(int i=0; i<4; ++i){dfs( x+fa1[i], y+fa2[i] );}
}
int main()
{cin>>T;while(T--){for(int i=0; i<N; i++)for(int j=0; j<N; ++j) arr[i][j]=0;cin>>m>>n;for(int i=1; i<=m; ++i){string str;cin>>str;for(int j=1; j<=n; ++j) arr[i][j] = str[j-1]-'0';}q.emplace(0,0); // 廣搜bfs();int sum=0;for(int i=1; i<=m; ++i){for(int j=1; j<=n; ++j){if(arr[i][j]!=1||arr[i+1][j]!=2) continue; // 跳過sum++;dfs(i,j); // 將這個島嶼統一染色。染成2}}cout<<sum<<endl;}return 0;
}
Java版
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;public class Main {static final int N = 102;static int T, m, n;static int[][] arr = new int[N][N];static int[] fa1 = {0, 0, 1, -1, 1, 1, -1, -1};static int[] fa2 = {1, -1, 0, 0, -1, 1, -1, 1};static Queue<node> q = new LinkedList<>();static class node {int x;int y;node(int cx, int cy) {x = cx;y = cy;}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);T = scanner.nextInt();while (T-- > 0) {for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {arr[i][j] = 0;}}m = scanner.nextInt();n = scanner.nextInt();for (int i = 1; i <= m; i++) {String str = scanner.next();for (int j = 1; j <= n; j++) {arr[i][j] = str.charAt(j - 1) - '0';}}q.add(new node(0, 0));bfs();int sum = 0;for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {if (arr[i][j] != 1 || arr[i + 1][j] != 2) {continue;}sum++;dfs(i, j);}}System.out.println(sum);}scanner.close();}static void bfs() {while (!q.isEmpty()) {int x = q.peek().x;int y = q.peek().y;q.remove();if (arr[x][y] != 0) {continue;}if (x < 0 || x > m + 1 || y < 0 || y > n + 1) {continue;}arr[x][y] = 2;for (int i = 0; i < 8; i++) {if (x + fa1[i] < 0 || x + fa1[i] > m + 1 || y + fa2[i] < 0 || y + fa2[i] > n + 1) {continue;}q.add(new node(x + fa1[i], y + fa2[i]));}}}static void dfs(int x, int y) {if (arr[x][y] != 1) {return;}if (x < 0 || x > m + 1 || y < 0 || y > n + 1) {return;}arr[x][y] = 3;for (int i = 0; i < 4; i++) {dfs(x + fa1[i], y + fa2[i]);}}
}

7、子串簡寫

問題描述

程序猿圈子里正在流行一種很新的簡寫方法:對于一個字符串,只保留首尾字符,將首尾字符之間的所有字符用這部分的長度代替。例如 internation-alization 簡寫成 i18n,Kubernetes (注意連字符不是字符串的一部分)簡寫成 K8s, Lanqiao 簡寫成 L5o 等。

在本題中,我們規定長度大于等于?K?的字符串都可以采用這種簡寫方法(長度小于?K?的字符串不配使用這種簡寫)。

給定一個字符串?SS?和兩個字符?c1??和?c2??,請你計算?S?有多少個以?c1??開頭?c2??結尾的子串可以采用這種簡寫?

輸入格式

第一行包含一個整數?K。

第二行包含一個字符串?S?和兩個字符?c1??和?c2?。

輸出格式

一個整數代表答案。

樣例輸入

4
abababdb a b

樣例輸出

6

樣例說明

符合條件的子串如下所示,中括號內是該子串:

[abab][abab]abdb

[ababab][ababab]db

[abababdb][abababdb]

ab[abab][abab]db

ab[ababdb][ababdb]

abab[abdb][abdb]

評測用例規模與約定

對于?20?的數據,2≤K≤∣S∣≤10000。

對于?100?的數據,2≤K≤∣S∣≤5×10^5。S?只包含小寫字母。c1??和 c2??都是小寫字母。

∣S∣?代表字符串?S?的長度。

// 注:查看二分解法lower_bound()..
// 我看網上很多人都用的是二分解法,這讓我迷惑不已,二分???
// 這道題目,用前綴和解決他不香嗎??
/*
? 其實,本題只需要維護一個數組vec[N]
? N表示,截止到這個位置,包括這個位置,到底有幾個字符c1,
? ----------
? abababdb
? 11223333 - 數字代表該下標之前有幾個C1,包括自己
? ----------
? 在遍歷第二遍,從k-1開始,當遇到b時,直接arr(下標-k+1),求這個位置,有幾個能匹配成a--b;然后累加。
? 沒嘍
*/

C++版
#include <iostream>
#include <vector>#define ll long long
using namespace std;
const ll N = 5e5+5;
int main()
{int k;cin>>k;string str;char c1,c2;cin>>str;cin>>c1>>c2;vector<ll> vec(N,0);if(str[0]==c1) vec[0]++;ll sum=0;for(int i=1; i<str.size(); ++i){if(str[i]==c1) vec[i]=vec[i-1]+1;else vec[i]=vec[i-1];}for(int i=k-1; i<str.size(); ++i){if(str[i]==c2) sum+=vec[i-(k-1)];}cout<<sum<<endl;return 0;
}
Java版
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 讀取間隔長度 kint k = scanner.nextInt();// 讀取字符串String str = scanner.next();// 讀取兩個字符 c1 和 c2char c1 = scanner.next().charAt(0);char c2 = scanner.next().charAt(0);scanner.close();long[] vec = new long[str.length()];// 初始化前綴和數組的第一個元素if (str.charAt(0) == c1) {vec[0] = 1;}// 計算前綴和數組for (int i = 1; i < str.length(); i++) {if (str.charAt(i) == c1) {vec[i] = vec[i - 1] + 1;} else {vec[i] = vec[i - 1];}}long sum = 0;// 遍歷字符串,統計滿足條件的組合數量for (int i = k - 1; i < str.length(); i++) {if (str.charAt(i) == c2) {sum += vec[i - (k - 1)];}}System.out.println(sum);}
}

8、整數刪除

問題描述

給定一個長度為?NN?的整數數列:A1,A2,…,AN。你要重復以下操作?K?次:

每次選擇數列中最小的整數(如果最小值不止一個,選擇最靠前的),將其刪除。并把與它相鄰的整數加上被刪除的數值。

輸出?K?次操作后的序列。

輸入格式

第一行包含兩個整數?N?和?K。

第二行包含?N?個整數,A1,A2,A3,…,AN?。

輸出格式

輸出?N?K 個整數,中間用一個空格隔開,代表?K?次操作后的序列。

樣例輸入

5 3
1 4 2 8 7

樣例輸出

17 7

樣例說明

數列變化如下,中括號里的數是當次操作中被選擇的數:

[1]?4?2?8?7

5?[2]?8?7

[7]?10?7

17?7

評測用例規模與約定

對于?20?的數據,1≤K<N≤10000。

對于?100?的數據,1≤K<N≤5×10^5,0≤Ai≤10^8。


// 悠悠的嘆息聲?為何?先敬羅衣,后敬魂
// 說實話,本題一看,我就知道,本題跟優先隊列有關,但是有一件挺無奈的事情是他要不停的刪除東西...
// 眾多周知,priority_queue內部就是一個黑盒(會用就行-其實內部是堆heap)
// 所以本題的難度就是,如何將priority_queue內部的東西,邊用邊刪除
// 當然,這里面,也有一個非常重要的細節!!!priority_queue重載的時候,要分情況重載,因為這是一個有序數組。
/*
? 其實也不難的,有人說用并查集,其實鏈表就能解決
? 只能說,這道題,你知道大概解法,你就能做,不知道,你肯定不會
? 本題需要維護一個 雙向鏈表(可不是標準鏈表)是用兩個數組模擬一個鏈表pre[N],ne[N];
? 然后在維護一個數組(sum[i]),去記錄那些值改變了
? 切記,設置鏈表的時候,最開始位置,從1開始。方便 減1 or 加1
? 假設,刪掉下標為cur的值(num)的時候
? pre[ne[y]]=pre[y]; ne[pre[y]]=ne[y]; 切記(不是!不是!sum[cur-1]+=num,sum[cur+1]+=num;,導致鏈接錯誤
? 既然刪掉該值了,吶本坐標,也就不再存在了,所以接下來 要改變坐標。
? 這時,你要將cur儲存的 下一位的坐標--傳給左邊
? 將cur儲存的上一個位置---傳遞給右邊
? ------------
? pre 0 1 2 3
? val 1 2 3 4
? ne ?2 3 4 5
? ------------
? 刪掉val值2之后,1和3的pri與ne都回改變
? ------------
? pre 0 1 1 3
? val 1 2 3 4
? ne ?3 3 4 5
? ------------
? OK了,其他的,想必大家就清楚了
? 切接,priority_queue默認大堆頂
*/

C++版
#include <iostream>
#include <vector>
#include <queue>
#define ll long long
const ll N = 1e6+5;
ll n,k;
using namespace std;
vector<ll> pre(N, 0);
vector<ll> ne(N, 0);
vector<ll> sum(N,0);
vector<ll> a(N,0);
// 重載
struct cmp{ // !!!bool operator() (const pair<ll,ll>& p1, const pair<ll,ll>& p2){if(p1.first == p2.first) return p1.second > p2.second; // 值相同選下標小的return p1.first > p2.first; }
};int main() // 天吶,這些都是什么東西   ·
{cin>>n>>k;priority_queue<pair<ll,ll>,vector<pair<ll,ll>>,cmp> p;ne[0]=1;for(int i=1; i<=n; ++i){ll val;cin>>val;p.emplace(val,i);//pre[i]=i-1;ne[i] =i+1;}while(k--){ // 進行k次操作ll val = p.top().first;ll y = p.top().second;while(sum[y]){val += sum[y];p.pop();p.emplace(val,y);sum[y]=0;// 更新val = p.top().first;y = p.top().second;}// 鏈表更新錯誤sum[pre[y]]+=val;sum[ne[y]]+=val;// 更新pre[ne[y]]=pre[y];ne[pre[y]]=ne[y];p.pop();}// 提取出來while(!p.empty()){a[p.top().second]=p.top().first;p.pop();}for(int i=1; i<=n; i++)if(a[i]+sum[i]) cout<<a[i]+sum[i]<<" ";return 0;
}
Java版
import java.util.PriorityQueue;
import java.util.Scanner;// 自定義比較器,用于優先隊列
class PairComparator implements java.util.Comparator<Pair> {@Overridepublic int compare(Pair p1, Pair p2) {if (p1.val == p2.val) {return Long.compare(p1.index, p2.index);}return Long.compare(p1.val, p2.val);}
}// 定義 Pair 類,用于存儲值和索引
class Pair {long val;long index;Pair(long val, long index) {this.val = val;this.index = index;}
}public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);long n = scanner.nextLong();long k = scanner.nextLong();long[] pre = new long[(int) (n + 2)];long[] ne = new long[(int) (n + 2)];long[] sum = new long[(int) (n + 2)];long[] a = new long[(int) (n + 2)];// 優先隊列,使用自定義比較器PriorityQueue<Pair> p = new PriorityQueue<>(new PairComparator());ne[0] = 1;for (int i = 1; i <= n; i++) {long val = scanner.nextLong();p.offer(new Pair(val, i));pre[i] = i - 1;ne[i] = i + 1;}// 進行 k 次操作for (long i = 0; i < k; i++) {Pair top = p.poll();long val = top.val;long y = top.index;while (sum[(int) y] != 0) {val += sum[(int) y];sum[(int) y] = 0;p.offer(new Pair(val, y));top = p.poll();val = top.val;y = top.index;}sum[(int) pre[(int) y]] += val;sum[(int) ne[(int) y]] += val;pre[(int) ne[(int) y]] = pre[(int) y];ne[(int) pre[(int) y]] = ne[(int) y];}// 提取優先隊列中的元素while (!p.isEmpty()) {Pair pair = p.poll();a[(int) pair.index] = pair.val;}// 輸出結果boolean first = true;for (int i = 1; i <= n; i++) {if (a[i] + sum[i] != 0) {if (!first) {System.out.print(" ");}System.out.print(a[i] + sum[i]);first = false;}}System.out.println();scanner.close();}
}

后面的兩道題,咱時間有限,就先跳過啦(~ ̄▽ ̄)~

要學會做減法。

當然大家有好的代碼、解析,也可以發給我,讓我瞅瞅。( ?? ω ?? )?,我貼上去。

知識點

1、二分查找

二分查找主要位于<algorithmn>頭文件中。常用的有lower_bound、upper_bound、binary_bound

lower_bound

查找第一個大于等于lower_bound的元素

auto it = lower_bound(vec.begin(),vec.end(),num); 
int place = it-vec.begin();
upper_bound

查找最后一個大于lower_bound的元素

auto it = upper_bound(vec.begin(),vec.end(),num); 
int place = it-vec.begin();
cout<<*it<<(it-vec.begin())<<endl; // 兩者皆可
binary_bound

判斷目標值,是否存在

bool t = binary_bound(vec.begin(),vec.end(),num); 
set中也設置了lower_bound();

?

set<int> s;
...
auto it = s.lower_bound(11);

2、emplace

C++中,emplace是 C++11引入的,用于在容器中構建元素,避免復制/移動操作,提高效率。

基本用法
container.emplace(args...);
與push的區別

push: 先構造對象,可能會傳遞。
emplace:傳參、直接構造。
(具體用法的話,就是原本push怎么用,emplace就代替成push。
如:push_back--emplace_back)
(push--emplace)

std::vector<std::pair<int, int>> vec;
// 傳統 push_back
vec.push_back(std::make_pair(1, 2));
vec.push_back({3, 4});  // C++11后可簡化// 使用 emplace_back
vec.emplace_back(5, 6);  // 直接構造 pair,更高效
適用容器
  • 支持?emplace?的容器:

    • 序列容器:vectordequelist
    • 關聯容器:mapsetunordered_mapunordered_set
    • 適配器:priority_queue(底層容器支持即可)
  • 不支持?emplace?的容器:

    • queuestack?等適配器(接口限制)。

3、log() ::cmath::

在 C++ 里,log()?函數一般是指?<cmath>?頭文件里用于進行對數運算的函數。下面會詳細介紹?log()?函數的具體用法。

log()?函數原型

<cmath>?頭文件里定義了幾個不同版本的?log()?函數,常用的有:

// 計算自然對數(以 e 為底)
double log(double x);
float log(float x);
long double log(long double x);

這些函數接收一個浮點數參數?x,并返回?x?的自然對數(以自然常數?e?為底)。

以其他底數計算

其他對數函數

除了?log()?函數,<cmath>?頭文件還提供了其他對數相關的函數:(C++11及之后適用)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899966.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899966.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899966.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

批量將文本文件轉換為 Word/PDF/Excel/圖片等其它格式

工作中我們經常會接觸到各種格式的文本文檔&#xff0c;比如說 txt 記事本文件、json文件、HTML格式文件等等。通常也會需要將文本文件轉換為其他的格式&#xff0c;比如說將文本文件轉換為 word 格式、PDF格式或者圖片格式等等。當我們想要對文本文件格式進行批量轉換的時候&a…

Java常用工具算法-2--加密算法1--對稱加密算法(推薦AES算法)

1、定義與核心原理 定義&#xff1a;加密和解密使用相同密鑰的算法。工作流程&#xff1a; 秘鑰協商&#xff1a;雙方需提前通過安全信道共享密鑰。加密過程&#xff1a;發送方用密鑰對明文加密&#xff0c;生成密文。解密過程&#xff1a;接收方用相同密鑰對密文解密&#xf…

WPS宏開發手冊——Excel常用Api

目錄 系列文章4、Excel常用Api4.1、判斷是否是目標工作excel4.2、獲取源工作表和目標工作表的引用4.3、獲取單元格的值4.4、設置單元格的值4.5、合并單元格4.6、獲取源范圍4.7、獲取源范圍行數4.8、通過源來獲取單元格的值4.9、設置單元格的背景顏色4.10、設置單元格的文字顏色…

安徽京準:GPS北斗衛星校時服務器助力大數據云計算

安徽京準&#xff1a;GPS北斗衛星校時服務器助力大數據云計算 安徽京準&#xff1a;GPS北斗衛星校時服務器助力大數據云計算 GPS北斗衛星校時服務器在大數據與云計算系統中發揮著關鍵作用&#xff0c;其通過提供高精度、高可靠的時間同步服務&#xff0c;解決了分布式系統的核…

音視頻 ColorSpace色彩空間詳解

前言 基于前篇介紹YUV格式,本文繼續介紹另一個重要概念顏色空間,又叫色彩空間;主要用于在音視頻開發中的色彩空間轉換。 色彩空間Color Space 色彩空間由色彩模型和色域共同定義。當色彩模型與特定的描述相關聯以后,就稱為色彩空間。 色彩模型Color Model 色彩模型Col…

高效定位 Go 應用問題:Go 可觀測性功能深度解析

作者&#xff1a;古琦 背景 自 2024 年 6 月 26 日&#xff0c;阿里云 ARMS 團隊正式推出面向 Go 應用的可觀測性監控功能以來&#xff0c;我們與程序語言及編譯器團隊攜手并進&#xff0c;持續深耕技術優化與功能拓展。這一創新性的解決方案旨在為開發者提供更為全面、深入且…

構造超小程序

文章目錄 構造超小程序1 編譯器-大小優化2 編譯器-移除 C 異常3 鏈接器-移除所有依賴庫4 移除所有函數依賴_RTC_InitBase() _RTC_Shutdown()__security_cookie __security_check_cookie()__chkstk() 5 鏈接器-移除清單文件6 鏈接器-移除調試信息7 鏈接器-關閉隨機基址8 移除異常…

大語言模型開發框架——LangChain

什么是LangChain LangChain是一個開發由語言模型驅動的應用程序的框架&#xff0c;它提供了一套工具、組件和接口&#xff0c;可以簡化構建高級語言模型應用程序的過程。利用LangChain可以使應用程序具備兩個能力&#xff1a; 上下文感知 將語言模型與上下文&#xff08;提示…

自動化釋放linux服務器內存腳本

腳本說明 使用Linux的Cron定時任務結合Shell腳本來實現自動化的內存釋放。 腳本用到sync系統命令 sync的作用&#xff1a;sync 是一個 Linux 系統命令&#xff0c;用于將文件系統緩存中的數據強制寫入磁盤。 在你執行reboot、poweroff、shutdown命令時&#xff0c;系統會默認執…

Python Websockets庫深度解析:構建高效的實時Web應用

引言 在現代Web開發中&#xff0c;實時通信已經成為許多應用的核心需求。無論是聊天應用、在線游戲、金融交易平臺還是協作工具&#xff0c;都需要服務器和客戶端之間建立持久、雙向的通信通道。傳統的HTTP協議由于其請求-響應模式&#xff0c;無法有效滿足這些實時交互需求。…

【實用技巧】電腦重裝后的Office下載和設置

寫在前面&#xff1a;本博客僅作記錄學習之用&#xff0c;部分圖片來自網絡&#xff0c;如需引用請注明出處&#xff0c;同時如有侵犯您的權益&#xff0c;請聯系刪除&#xff01; 文章目錄 前言下載設置總結互動致謝參考目錄導航 前言 在數字化辦公時代&#xff0c;Windows和…

Node.js 技術原理分析系列 —— Node.js 調試能力分析

Node.js 技術原理分析系列 —— Node.js 調試能力分析 Node.js 作為一個強大的 JavaScript 運行時環境,提供了豐富的調試能力,幫助開發者診斷和解決應用程序中的問題。本文將深入分析 Node.js 的調試原理和各種調試技術。 1. Node.js 調試原理 1.1 V8 調試器集成 Node.js…

【圖論】最短路徑問題總結

一圖勝千言 單源最短路徑 正權值 樸素Dijkstra dijkstra算法思想是維護一個永久集合U&#xff0c;全部點集合V。 循環n -1次 從源點開始&#xff0c;在未被訪問的節點中&#xff0c;選擇距離源點最近的節點 t。 以節點 t 為中間節點&#xff0c;更新從起點到其他節點的最短…

【最佳實踐】win11使用hyper-v安裝ubuntu 22/centos,并配置固定ip,掃坑記錄

文章目錄 場景查看本機的win11版本啟用hyper-vhyper-v安裝ubuntu22虛擬機1.準備好個人的 iso文件。2. hyper-v 快速創建3.編輯設置分配內存自定義磁盤位置設置磁盤大小連接網絡修改虛擬機名稱自定義檢查點位置 和智能分頁件位置虛擬機第一次連接給ubuntu22配置固定ip遇到過的坑…

自然語言處理(25:(終章Attention 1.)Attention的結構?)

系列文章目錄 終章 1&#xff1a;Attention的結構 終章 2&#xff1a;帶Attention的seq2seq的實現 終章 3&#xff1a;Attention的評價 終章 4&#xff1a;關于Attention的其他話題 終章 5&#xff1a;Attention的應用 目錄 系列文章目錄 前言 Attention的結構 一.seq…

Git 命令大全:通俗易懂的指南

Git 命令大全&#xff1a;通俗易懂的指南 Git 是一個功能強大且廣泛使用的版本控制系統。對于初學者來說&#xff0c;它可能看起來有些復雜&#xff0c;但了解一些常用的 Git 命令可以幫助你更好地管理代碼和協作開發。本文將介紹一些常用的 Git 命令&#xff0c;并解釋它們的…

基于yolov11的棉花品種分類檢測系統python源碼+pytorch模型+評估指標曲線+精美GUI界面

【算法介紹】 基于YOLOv11的棉花品種分類檢測系統是一種高效、準確的農作物品種識別工具。該系統利用YOLOv11深度學習模型&#xff0c;能夠實現對棉花主要品種&#xff0c;包括樹棉&#xff08;G. arboreum&#xff09;、海島棉&#xff08;G. barbadense&#xff09;、草棉&a…

論文:Generalized Category Discovery with Clustering Assignment Consistency

論文下載&#xff1a; https://arxiv.org/pdf/2310.19210 一、基本原理 該方法包括兩個階段:半監督表示學習和社區檢測。在半監督表示學習中&#xff0c;使用了監督對比損失來充分地推導標記信息。此外&#xff0c;由于對比學習方法與協同訓練假設一致&#xff0c;研究引入了…

Java高級JVM知識點記錄,內存結構,垃圾回收,類文件結構,類加載器

JVM是Java高級部分&#xff0c;深入理解程序的運行及原理&#xff0c;面試中也問的比較多。 JVM是Java程序運行的虛擬機環境&#xff0c;實現了“一次編寫&#xff0c;到處運行”。它負責將字節碼解釋或編譯為機器碼&#xff0c;管理內存和資源&#xff0c;并提供運行時環境&a…

MySQL 5.7 Online DDL 技術深度解析

14.13.1 在線DDL操作 索引操作主鍵操作列操作生成列操作外鍵操作表操作表空間操作分區操作 索引操作 下表概述了對索引操作的在線DDL支持情況。星號表示有附加信息、例外情況或依賴條件。有關詳細信息&#xff0c;請參閱語法和使用說明。 操作原地執行重建表允許并發DML僅修…