ap325第二章
第二章 排序
- sort
sort()除了前兩個參數指定範圍之外,還可以傳入
第三個參數,第三個參數是一個比較函數,這個函數負責告訴sort()排序的順序是什
麼:比較函數必須傳入兩個參數,如果第一個參數要排在第二個的前面就回傳true,
否則回傳false。
簡單來說:1
2
3bool cmp(int j,int i){
return j>i;
}
- 心血來潮寫的leetcode每日一題
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35class Solution {
public:
vector<int> findThePrefixCommonArray(vector<int>& A, vector<int>& B) {
int n=A.size();
int m=n;
int P_A[52]={};
int P_B[52]={};
int Aarr[52];
int Barr[52];
vector<int> ans; //寫成vector<int> ans(n,0)的話意思是大小為n,數據為0
int num=0;
for(int i=n-1;i>=0;i--){
Aarr[i]=A.back();
Barr[i]=B.back();
A.pop_back();
B.pop_back();
}
for(int i=0;i<n;i++){
P_A[Aarr[i]]+=1;
P_B[Barr[i]]+=1;
if(P_A[Aarr[i]]>0 && P_B[Aarr[i]]>0){
num++;
P_A[Aarr[i]]--;
P_B[Aarr[i]]--;
}
if(P_A[Barr[i]]>0 && P_B[Barr[i]]>0){
num++;
P_A[Barr[i]]--;
P_B[Barr[i]]--;
}
ans.push_back(num);
}
return ans;
}
};
二分搜基本寫法
1 |
|
跳躍式二分搜基本寫法
- 本段為找到第一個大於x的位置
1
2
3
4
5
6
7
8
9
10int jump_search(int a[],int n,int x){
if(a[0]>=x) return 0;
int po=0;
for(int jump=n/2;jump>0;jump/=2){
while(po+jump<n && a[po+jump]<x){
po+=jump;
}
}
return po+1;
}
C++函數
binary_search()
lower_bound()
upper_bound()
其實只要學會使用lower_bound(),這個函數是用來找到第一個大於等於某個值的位
置,另外兩個都很像,upper_bound()找到第一個大於某值的位置,而
binary_search()是只傳回是否找到。
lower_bound(first, last, t);
要求在[first, last)
的範圍內以二分搜找到第一個大於或等於t的值,找到時回傳
所在位置,找不到時(t比最後一個大),回傳last。以下有幾點請注意:
- 找到時回傳的是位置,要得到陣列索引值就將它減去陣列起始位置。
- 呼叫
lower_bound()
必須確定搜尋範圍是排好序的,如果你針對亂七八糟的資料
以lower_bound()
去進行二分搜,只會得到亂七八糟的結果,編譯器是不會告訴
你沒排序的。 - 搜尋範圍是傳統的左閉右開區間,也就是不含last;
- 找不到時回傳last,通常這個位置是超過陣列的範圍,所以沒確定找到時不可以
直接去引用該位置的值。
例題2-2
https://judge.tcirc.tw/problem/d011
自己寫的解:
1 |
|
教科書解:
1 |
|
- 答案細節:
C++ 中,陣列作為函數參數時,是以指標形式傳遞的。也就是說,當你將 b 作為參數傳入函數 distinct() 時,實際上傳遞的是 b 的記憶體地址。因此,distinct() 函數內對 b 陣列的操作會直接修改原來的 b 陣列的內容。
所以b會自動改變。
用map實作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using namespace std;
typedef long long LL;
int main()
{
int a[N],n,k;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
}
map<int,int> S; //map第一位是key,第二位是值 first,second
for(int i=0;i<n;i++){
S[a[i]]=0;
}
int r=0;
for(auto it=S.begin();it!=S.end();++it){ //it是迭代器,指向 map 中的每個鍵值對。
it->second=r++;
}
for(int i=0;i<n;i++){
a[i]=S.find(a[i])->second;
}
for(int i=0;i<n;i++){
cout<<a[i]<<' ';
}
return 0;
}
關於stl資料結構(DS)
https://zrn-code.github.io/2020/07/18/STL/
2-3快速冪
https://judge.tcirc.tw/problem/d012
最簡單寫法:
1 |
|
但實在跑太慢拉~
運用分治、遞迴把它優化一下:
O(y)->O(log y)
1 |
|
2-4快速冪200位
https://judge.tcirc.tw/problem/d013
1 |
|
當x大到超過long long的時候,我們需要先求x除以p的餘
數,而除以p的餘數可以一位一位的累加計算。例如x=123, p=5,x可以看成
(1*10+2)10+3,所以x除以p的餘數也就等於
((((110%p+2)%p)*10)%p+3)%p。
所以要邊把string轉成int邊mod
2-6Two-Number problem
https://judge.tcirc.tw/problem/d015
用二分搜加速搜尋速度以A的元素在B中滑動找解
1 |
|
順便附上剛剛寫的leetcode minimize-xor
https://leetcode.com/problems/minimize-xor/
這題可以練習bit運算1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60class Solution {
public:
long long result;
void cmp(int total1, int total2, vector<int> v) {
if (total1 == total2) {
long long i = 1;
while (!v.empty()) {
result += i * v.back();
i *= 2;
v.pop_back();
}
return;
}
if (total1 > total2) {
for (int i = v.size() - 1; i >= 0; i--) {
if (v[i] == 1) {
v[i] = 0;
cmp(total1 - 1, total2, v);
break;
}
}
/*for (auto &t : v) {
if (t == 1) {
t = 0;
cmp(total1 - 1, total2, v);
break;
}
}*/
} else if (total1 < total2) {
for (int i = v.size() - 1; i >= 0; i--) {
if (v[i] == 0) {
v[i] = 1;
cmp(total1 + 1, total2, v);
break;
}
}
}
}
int minimizeXor(int num1, int num2) {
result = 0;
int total1 = 0;
int total2 = 0;
while (num2 > 0) {
total2 += num2 % 2;
num2 /= 2;
}
vector<int> v;
while (num1 > 0) {
total1 += num1 % 2;
v.push_back(num1 % 2);
num1 /= 2;
}
reverse(v.begin(), v.end());
while (v.size() < 32) {
v.insert(v.begin(), 0);
}
cmp(total1, total2, v);
return result;
}
};leetcode動態規劃、遞迴
https://leetcode.com/problems/combination-sum-iv/
我的菜菜寫法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Solution {
public:
int recursion(int n, vector<int> nums, vector<int>& dp) {
if (n < 0) {
return 0;
}
if (n == 0) {
return 1;
}
if (dp[n] != -1) {
return dp[n]; // 如果已經計算過,直接返回結果
}
int result = 0;
for (auto k : nums) {
result += recursion(n - k, nums, dp); // 累加所有可能的結果
}
dp[n] = result; // 記錄計算結果以提高效率
return result;
}
int combinationSum4(vector<int>& nums, int target) {
vector<int> dp(target + 1, -1); // 動態規劃數組,初始化為 -1
return recursion(target, nums, dp);
}
};動態規劃更強的寫法:
1
2
3
4
5
6
7
8
9
10
11
12
13class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<unsigned int> dp(target+1,0);
dp[0]=1;
for(int i=1;i<=target;i++){
for(int j:nums){
if(i-j>=0) dp[i]+=dp[i-j];
}
}
return dp[target];
}
};外層迴圈:
for(int i = 1; i <= target; i++)
,表示我們從 1 一直計算到target
,對每個目標值i
,我們都試圖通過減去 nums 中的數字來達到這個目標。
內層迴圈:for(int j : nums)
,表示對於nums
中的每一個數字j
,如果i - j >= 0
,即當i - j
是有效的(不小於 0)時,我們將dp[i - j]
的值加到dp[i]
上。
這是因為,如果我們可以從i - j
達到i
,那麼所有能達到i - j
的組合都可以通過再加上j
來達到i
。
2-6set寫法
https://judge.tcirc.tw/problem/d015
1 |
|
2-7互補團隊
https://judge.tcirc.tw/problem/d016
自己練習,tle的答案,感覺寫得很亂,進步空間很大
1 |
|
看提示寫的:
1 |
|
2-9子集合乘積(折半枚舉)
https://judge.hlhs.hlc.edu.tw/ShowProblem?problemid=e036
不足以通過這一題所有測資。這個程式的方法也很直接,我們逐一考慮每一個
元素,以一個map M1來記錄著目前所有可能的子集合乘積,相同的乘積只記錄一筆,
但記錄能產生此乘積的子集合個數。對於下一個元素a[i],把原有的所有可能在乘上
此新元素,就是新的可能乘積。在計算過程,新的乘積不能直接加到原來的裡面,否則
無法分辨新舊。因此要利用一個 map M2 來暫存,當一個元素處理完畢後交換 M1 與
M2,以便下一個元素時再從M1計算到M2。
(我看不懂…)
1 | // subset product = 1 mod P, slow method |
接下來要來學折半枚舉