ap325第一章
自學筆記
big(O)
10^9
是2^31
次方bits
內,也就是int
的大小(int可容納2*10^9)- 大寫轉小寫(ASCII code)
1
2
3
4
5
6char ch='A';
for(int i=0;i<32;i++){ //兩者差32
ch++;
}
cout<<ch; - 進行位元運算時
1
2
3
4
5for(int i=0;i<n;i++){
if(1<<i&k){ //k放的要是十進制,而不是像是二進制的值
ans++; //ex:放10而不是1010
}
} - 10進制轉2進制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using namespace std;
int main()
{
int n;
cin>>n;
vector<int> v;
while(n){
v.push_back(n%2);
n/=2;
}
while(!v.empty()){ //轉換出來會是倒過來的下面是證明
cout<<v.back();
v.pop_back();
}
return 0;
} - 降低時間複雜度:
1
2
3
4
5
6
7
8
9
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
// code here
return 0;
}
kmp字串演算法
可以讓字串比對從O(mn)簡化為O(m+n)
next序列為尋找相同前後綴長度,next序列的定義過程是其中最難的部分,關鍵:前後綴迭代
例題:
https://leetcode.com/problems/find-the-index-of-the-first-occurrence-in-a-string/
1 | class Solution { |
以下這題:
s = “abcabcabcabc” 对应 next 数组为:000 123 456 789,s.length() = 12 = 9(next 数组最后一位) + 3(重构子串长度),3 是 s长度的因数,所以 s 能被子串重构
https://leetcode.com/problems/repeated-substring-pattern/
1 | class Solution { |
第一章
遞迴(recursive)
遞迴中使用容器或是陣列要格外小心,因為寫錯的話可能會因為沒有正確回溯而失敗
ex:1
2void rec(string s, vector<bool> &used) //上面這個所有rec會共用同一個容器,記憶體會比較小,但操作容易出錯
void rec(string s, vector<bool> used) //下面這個每一次rec都會創造一個used記憶體較大,但除錯較簡單ex:
1
void rec(string s, int t[]) //會共用容器,因為c++中傳遞陣列參數,陣列參數會退化為指標
1-3支點切割:
線性搜尋解答: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
using namespace std;
typedef long long LL;
LL s[50010];
LL cut(int left,int right){
if(right-left<=1) return 0;
LL k=(s[right]-s[left])/2;
LL beginn=left;
while(s[beginn]<=s[left]+k){
beginn++;
}
if(s[beginn-1]-s[left]>=s[right]-s[beginn]){
beginn--;
}
LL len=s[right]-s[left];
return len+cut(left,beginn)+cut(beginn,right);
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int i,n,l;
cin>>n>>l;
s[0]=0;
s[n+1]=l;
for(i=1;i<=n;i++){
cin>>s[i];
}
cout<<cut(0,n+1);
return 0;
}跳躍式二分搜:
跳躍式二分搜解答:
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
using namespace std;
typedef long long LL;
LL s[50010];
LL cut(int left,int right){
if((right-left)<=1) return 0;
int m=left;
LL k=s[left]+(s[right]-s[left])/2;
for(int jump=(right-left)/2;jump>0;jump/=2){
while(m+jump<right && s[m+jump]<k){
m+=jump;
}
}
if(s[m]-s[left]<s[right]-s[m+1]){
m++;
}
return s[right]-s[left]+cut(left,m)+cut(m,right);
}
int main() {
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int i,n,l;
cin>>n>>l;
s[0]=0;
s[n+1]=l;
for(i=1;i<=n;i++){
cin>>s[i];
}
cout<<cut(0,n+1);
return 0;
}
遞迴
- 支點切割
https://zerojudge.tw/ShowProblem?problemid=f638
寫了兩個小時還是錯的決定先休息,放這裡之後寫,寫了兩個小時發現我沒看完題目
放一個正解網址https://sites.google.com/view/zsgititit/home/jin-jiec-cheng-shi-she-ji/apcs201802%E7%AC%AC3%E9%A1%8C%E6%94%AF%E9%BB%9E%E5%88%87%E5%89%B2
1 |
|
1-6最接近區間和
- 假設陣列A[1..n]中存放著某些整數,另外給了一個整數K,請計算哪一個連續區
段的和最接近K而不超過K。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
using namespace std;
int K;
int main()
{
int n;
cin>>n>>K;
int s[n];
for(int i=0;i<n;i++){
cin>>s[i];
}
int sum=0;
int bestleft=0;
int bestright;
int bestans=1000;
int right;
int left=0;
for(int i=0;i<n;i++){
sum+=s[i];
right=i;
while(sum>K){
sum-=s[left];
left++;
}
if(abs(K-sum)<=abs(K-bestans)){
bestans=sum;
bestleft=left;
bestright=right;
}
}
cout<<bestleft<<' '<<bestright;
return 0;
}
1-7子集合乘積
https://judge.tcirc.tw/problem/d006
- 寫出這個答案的老師真的好厲害
以下為暴力解,所以當測試資料過大時會TLE: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
using namespace std;
int main()
{
int n;
int P=10009;
int A[26]; //集合最大只有25個元素,因為0<n<26
cin>>n;
int sum=0;
for(int i=0;i<n;i++){
cin>>A[i];
}
for(int i=1;i<(1<<n);i++){ // 1<<n的範圍就是2^n,所以這裡以binary i的數字1代表元素在內
long long prod=1;
for(int j=0;j<n;j++){
if(i & (1<<j)){ //如果binary i的第j位為一,則列進prod內
prod=(prod*A[j])%P; //先mod跟最後再mod根據公式結果一樣
}
}
if(prod==1){
sum++;
}
}
cout<<sum;
return 0;
}
以下為遞迴解,時間複雜度較小,如果是迴圈解複雜度為O(2^n)遞迴解的話O(n*2^n)
1 |
|
1-8子集合的和
https://judge.tcirc.tw/problem/d007
- 跟上面那題的解題思路大差不遠
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
using namespace std;
typedef long long LL;
LL P;
int A[26];
int n;
LL bestans=0;
void rec(int i,LL sum){
//cout<<i<<" i "<<bestans<<'\n';
if(sum>P){
return;
}
if(i>=n){
if(P-sum<P-bestans){
bestans=sum;
}
return;
}
rec(i+1,sum+A[i]);
rec(i+1,sum);
}
int main()
{
cin>>n;
cin>>P;
for(int i=0;i<n;i++){
cin>>A[i];
}
rec(0,0);
cout<<bestans;
return 0;
}
八皇后問題
- n皇后暴搜解答:
(時間複雜度會爆)
O(n2*n!)以下為遞迴解: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
using namespace std;
int nq(int n){
int P[14];
int total=0;
for(int i=0;i<n;i++){
P[i]=i;
}
do{
bool valid=true;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++)
if(abs(P[i]-P[j])==j-i){ //P[i]-P[j])==j-i可以比對是否在同一斜線
valid=false;
break;
}
}
if(valid){
total++;
}
}while(next_permutation(P,P+n)); //字典排序P到P+n
return total;
}
int main()
{
for(int i=1;i<12;i++){
cout<<nq(i)<<' ';
}
return 0;
}
O(n*n!)遞迴解21
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
using namespace std;
int nqr(int n,int k,int p[]){
int total=0;
if(k>=n){
return 1;
}
for(int i=0;i<n;i++){ //嘗試當前行的每一個位置
bool valid=true;
for(int j=0;j<k;j++){
if(p[j]==i || abs(i-p[j])==k-j){ //檢查是否與前面衝突
valid=false;
break;
}
}
if(!valid){
continue;
}
p[k]=i;
total+=nqr(n,k+1,p);
}
return total;
}
int main()
{
int p[15];
for(int i=1;i<12;i++){
cout<<nqr(i,0,p)<<' ';
}
return 0;
}
(降低複雜度)
比 O(n*n!) 低,因為會先標記後才進入遞迴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
using namespace std;
int nqr(int n,int k,int p[]){
if(k>=n) return 1;
bool valid[n];
int total=0;
for(int i=0;i<n;i++) valid[i]=true; // y軸
// mark
for(int j=0;j<k;j++){
valid[p[j]]=false; //p[j]是(p[j],j)的y軸數據
int i=p[j]+k-j; //i=y軸數據+(當前x軸-x軸數據) 往左下
if(i<n){
valid[i]=false;
}
i=p[j]-k+j; //往右上
if(i>=0){
valid[i]= false;
}
}
for(int i=0;i<n;i++){
if(valid[i]){
p[k]=i;
total+=nqr(n,k+1,p);
}
}
return total;
}
int main()
{
int p[15];
for(int i=1;i<12;i++){
cout<<nqr(i,0,p)<<' ';
}
return 0;
}
1-10最多得分的皇后
https://judge.tcirc.tw/problem/d008
- 延續第四天,然加上一個判斷要選哪幾列有皇后的排列就好(最佳得分並不一定是每一列皆有皇后)
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
60
using namespace std;
int point[15][15];
int best=0;
void qr(int i,int K,int p[],int bound,int total){
if(K>=bound){
if(total>best){
best=total;
}
return;
}
if(!(i & (1<<K))){
qr(i,K+1,p,bound,total);
return;
}
bool valid[bound];
for(int j=0;j<bound;j++){
valid[j]=true;
}
for(int j=0;j<K;j++){
if(i& (1<<j) ){
valid[p[j]]=false;
int up=p[j]+K-j; //i=y軸數據+(當前x軸-x軸數據) 往左下
if(up<bound){
valid[up]=false;
}
int down=p[j]-K+j; //往右上
if(down>=0){
valid[down]=false;
}
}
}
for(int j=0;j<bound;j++){
if(valid[j]){
p[K]=j;
qr(i,K+1,p,bound,total+point[j][K]);
}
}
}
int main()
{
int p[15];
int n;
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>point[i][j];
}
}
for(int i=1;i<(1<<n);i++){ //將1向左移動
int total=0;
qr(i,0,p,n,total);
}
cout<<best;
return 0;
}
1-10刪除矩形邊界
https://judge.tcirc.tw/problem/d009
- 有更快的dp寫法,不過這邊先用遞迴暴力解
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
60
61
62
63
64
65
66
67
68
69
70
71
72
using namespace std;
int s[13][13];
int best=100000;
void rec(int left_y,int left_x,int right_y,int right_x,int cost){
if(abs(left_x-right_x)==0 || abs(left_y-right_y)==0){
if(cost<best){
best=cost;
}
return;
}
int one=0;
int zero=0;
int ans=0;
for(int i=left_x;i<=right_x;i++){
if(s[left_y][i]==1){
one++;
}else{
zero++;
}
ans=min(one,zero);
}
rec(left_y+1,left_x,right_y,right_x,cost+ans); //1
one=0;
zero=0;
for(int i=left_y;i<=right_y;i++){
if(s[i][left_x]==1){
one++;
}else{
zero++;
}
ans=min(one,zero);
}
rec(left_y,left_x+1,right_y,right_x,cost+ans); //2
one=0;
zero=0;
for(int i=right_x;i>=left_x;i--){
if(s[right_y][i]==1){
one++;
}else{
zero++;
}
ans=min(one,zero);
}
rec(left_y,left_x,right_y-1,right_x,cost+ans); //3
one=0;
zero=0;
for(int i=right_y;i>=left_y;i--){
if(s[i][right_x]==1){
one++;
}else{
zero++;
}
ans=min(one,zero);
}
rec(left_y,left_x,right_y,right_x-1,cost+ans); //4
}
int main()
{
int m,n;
cin>>m>>n;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
cin>>s[i][j];
}
}
rec(0,0,m-1,n-1,0);
cout<<best;
return 0;
}
leetcode 遞迴題目
https://leetcode.com/problems/letter-tile-possibilities/?envType=daily-question&envId=2025-02-17
如果要找所有組合的話以下是遞迴範例(一定有其他更快的),考試複習這題不錯
1 | class Solution { |