\(ALGO\ [4]\ [5]\) 失蹤
這好像是我最後一堂小社 掰掰
雙指針
分治
二分搜
枚舉 就是暴搜啦哈哈哈
然後我不會 北市賽的三等獎就沒了 哈哈
但你真的會枚舉嗎
數字 | 二進位數字 | 子序列 |
0 | 000 | {∅} |
1 | 001 | {3} |
2 | 010 | {2} |
3 | 011 | {2, 3} |
4 | 100 | {1} |
5 | 101 | {1, 3} |
6 | 110 | {1, 2} |
7 | 111 | {1, 2, 3} |
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
int n;cin>>n;
vector<int> arr(n);
for(int i=0; i<n; i++) cin>>arr[i];
for(int i=0; i<(1<<n); i++){ //從0-(2^n-1)
for(int j=0; j<n; j++){
if((i>>j)&1) cout<<arr[j]<<' ';
//how to 檢測某個數字的第j個字元是不是1?
//把他右移j次 讓那個字元變成最小的那一位
//最後位元且 1 這樣如果那位是1 回傳1 否則0
}cout<<'\n';
}
}
# 位元枚舉
#include <bits/stdc++.h>
using namespace std;
vector<int> take(1000, 0);
int n;
void recur(int x, vector<int>& li){
if(x==-1){
for(int i=0; i<n; i++) if(take[i]) cout<<li[i]<<' ';
cout<<'\n';
}else{
take[x]=1; //這裡很重要
recur(x-1, li); //這裡超重要
take[x]=0; //這裡要記得
recur(x-1, li); //很多枚舉都要這樣寫
}
}
signed main(){
ios_base::sync_with_stdio(false);cin.tie(0);
cin>>n;
vector<int> li(n);
for(int i=0; i<n; i++) cin>>li[i];
recur(n-1, li);
}
#遞迴枚舉
來砍樹囉 (剪枝
來減肥囉 (減脂
. . .
. . .
. . .
. . .
雖然我們還沒講圖論
但等下會示範一下怎麼在網格上走路
#include<bits/stdc++.h>
using namespace std;
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};
vector<vector<int>> vis(9, vector<int>(9, 0));
//9是陣列分別的大小 0是陣列每一項的初始值
int cnt=0; //答案
string s; //指定操作
void enumerate(int n, int x, int y){
//n 走幾步了 / x 目前走到的格子的x座標 / y y座標
if(n==48&&x==7&&y==0){
cnt++;
}
int op=-1; // operation 操作
if(s[n]=='U') op=0;
if(s[n]=='D') op=1;
if(s[n]=='L') op=2;
if(s[n]=='R') op=3; //分別照要求操作
if(op!=-1){
if(vis[x+dx[op][y+dy[op]]) continue;
vis[x+dx[op]][y+dy[op]]=1; // 枚舉經典操作
enumerate(n+1, x+dx[op], y+dy[op]);
vis[x+dx[op]][y+dy[op]]=0;
}else{ //無要求 可以自定義
for(int i=0; i<4; i++){
if(vis[x+dx[i]][y+dy[i]]) continue;
vis[x+dx[i]][y+dy[i]]=1;
enumerate(n+1, x+dx[i], y+dy[i]);
vis[x+dx[i]][y+dy[i]]=0;
}
}
}
signed main(){
cin>>s;
for(int i=0; i<9; i++) vis[i][0]=1, vis[0][i]=1, vis[8][i]=1, vis[i][8]=1;
//初始化 因為vis=1代表不能走
//而我們要再(1, 1)~(7, 7)上走
/所以把外圍的都先封掉
//也可以避免走出陣列 RE
vis[1][1]=1;
enumerate(0, 1, 1);
cout<<cnt;
}
x
y
. . .
. . .
. . .
. . .
if(n==48){
if(x==6&&y==0) cnt++;
else return;
}
簡化一點點
只能往上下走但不能往左右走了
如果往上就永遠到不了終點
如果往下就永遠填不完上面的
要叫他貪心還是貪婪呢
根
0
1
1-3
1
1+2
1
3-4
3
3-6
3
this one
is good
根
0
1
1-3
1
1+2
1
3-4
3
3-6
3
this one
is good
\(i\) 先吃
\(i+1\) 先吃
\(i\) 耗時
\(i+1\) 耗時
\(W_{i+1}+C_{i+1}\)
\(W_i+W_{i+1}+C_{i+1}\)
\(W_{i}+C_{i}\)
\(W_i+W_{i+1}+C_{i}\)
>
>
>
#include <bits/stdc++.h>
using namespace std;
#define ss second
#define ff first
#define int long long
signed main() {
ios_base::sync_with_stdio(false);cin.tie(0);
int n;
while(true){
cin>>n;
if(n==0) return 0;
vector<pair<int, int>> people;
for(int i=0; i<n; ++i){
int a,b;
cin>>a>>b;
people.push_back({b, a});
}
sort(people.begin(), people.end(), greater<pair<int, int>>());
// pair會先比較第一項再比較第二項
int current=0; //廚師煮飯時間和
int maximum=0;
for(int i=0; i<n; ++i){
current=current+people[i].ss;
maximum=max(maximum, current+people[i].ff); // curTime + arr[i].first是第i人離開的時間
}
cout<<maximum<<'\n';
}
}
# PRESENTING CODE
為了不要讓上一條太深
2
3
5
5
10
15
7
8
25
合併 / 切割
2
3
5
5
10
15
7
8
25
2
3
5
5
10
15
7
8
25
2
3
5
5
10
15
7
8
25
2
3
5
5
10
15
7
8
25
綜上所述,我們使用貪婪策略可以確保最終只剩下一個數字,並且成本最小。這就是這個問題的一個有效解法。
#include <bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
int n,m;
while(cin>>n){
priority_queue<int,vector<int>,greater<int> > p;
while(n--){
cin>>m;
p.push(m);
}
int sum=0;
int a,b;
while(p.size()>1){
a=p.top();
p.pop();
b=p.top();
p.pop();
sum+=a+b;
p.push(a+b);
}
cout<<sum<<'\n';
p.pop();
}
}
# 誰先晚餐
記得開long long
additional
如果有時間再講
競程是一半的二分搜跟一半的低批
--不知道誰說的
但我比賽都寫圖論題欸
--cjtsai
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Query(5) => 太大
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Query(3) => 太小
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Query(4) => ANS!
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
Num
Query
所求
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
Num
Query
所求
int l=0, r=MAXN;
while(r>l){
int mid = (l+r)/2;
if(check(mid)){
r=mid;
}else{
l=mid+1;
}
}
int l=-1, r=MAXN;
while(r-1>l){
int mid = (l+r)/2;
if(check(mid)){
r=mid;
}else{
l=mid;
}
}
l
最後 l
是最後一個 0
r
是第一個 1
Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky. — Donald Knuth
小心不要寫爛掉ㄌ
因為binary search-2打不下
這邊姑且把表示二進位的方法當作是個binary
匹配成功!
#include <bits/stdc++.h>
using namespace std;
const int SIZE = 2e5 + 5;
const int KSIZ = 5005;
const int INF = 1e9+5;
int n, k;
int h[SIZE], w[KSIZ];
bool ok (int x) {
int pos = 1, len = 0;
for (int i = 1; i <= n; i++) {
if (h[i] >= x) len++;
else len = 0;
if (len == w[pos]) {
pos++;
len = 0;
}
if (pos > k) return 1;
}
return 0;
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> h[i];
for (int i = 1; i <= k; i++) cin >> w[i];
int l = 1, r = INF;
while (l < r) {
int mid = (l + r) / 2;
if (!ok (mid)) r = mid;
else l = mid + 1;
}
cout << l - 1 << '\n';
}
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, x;
vector<int> li(200007);
bool check(int h){
int cnt=0;
for(int i=0; i<n; i++){
if(h>li[i]){
cnt+=(h-li[i]);
}
}
return cnt<=x;
}
signed main(){
ios_base::sync_with_stdio(false);cin.tie(0);
int t;cin>>t;
while(t--){
cin>>n>>x;
for(int i=0; i<n; i++) cin>>li[i];
int l=1, r=10000000007;
while(l!=r-1){
int mid=(l+r)/2;
if(check(mid)){
l=mid;
}else{
r=mid;
}
}cout<<l<<'\n';
}
}
分治 分而治之
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 | 3 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 | 3 | 4 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 | 3 | 4 | 5 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 | 3 | 4 | 5 | 6 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
1 | 3 | 4 | 6 | 8 |
2 | 5 | 7 | 9 | 10 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
#include<bits/stdc++.h>
using namespace std;
void mergesort(int l,int r,vector<int> &data){
if(r-l==1) return ;
int mid=(l+r)/2,tmp_len=mid-l;
mergesort(l,mid,data),mergesort(mid,r,data);
vector<int> tmp(tmp_len);
for(int i=0;i<tmp_len;i++) tmp[i]=data[i+l];
for(int L=0,R=mid,i=l;L<tmp_len;i++){
if(tmp[L]<data[R]||R==r) data[i]=tmp[L++];
else data[i]=data[R++];
}
return ;
}
int main(){
int n;
cin>>n;
vector<int> data(n);
for(int i=0;i<n;i++) cin>>data[i];
mergesort(0,n,data);
for(int i=0;i<n;i++) cout<<data[i]<<" ";
cout<<endl;
}
分治好難
遞迴式
迭代式
#include<bits/stdc++.h>
#define int long long
using namespace std;
//inline 會加速,要不要家都行
//int ans=1 這是默認參數
//放在最後 呼叫時要不要傳入都行
inline int power(int x,const int &y,int ans=1){
for(int i=1;i<=y;i++,x=x*x%mod) if(i&y) ans=ans*x%mod;
return ans;
}
signed main(){
int x,y;
cin>>x>>y;
cout<<power(x,y)<<endl;
}
#include<bits/stdc++.h>
#define int long long
using namespace std;
int power(int x, int y){
if(y==0) return 1;
if(y==1) return x;
int tmp=power(x, y/2);
if(y%2) return tmp*tmp*x;
else return tmp*tmp;
}
signed main(){
int x,y;
cin>>x>>y;
cout<<power(x,y)<<endl;
}
我比較喜歡這個
謝謝807\(^2\)
雖然我覺得這條講了會燒雞
但他蠻重要ㄉ
K
K
K
K
K
K
K
聽說去年一三沒講雙指針
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
ios_base::sync_with_stdio(false);cin.tie(0);
int n, m;cin>>n>>m;
vector<int> li(n);
for(int i=0; i<n; i++) cin>>li[i];
int tot=li[0];
int fast=0, slow=0;// 我就是快慢指針
int cnt=0;
while(fast!=n){
while(tot>m){
tot-=li[slow];slow++;
}
cnt+=fast-slow+1;
fast++;
tot+=li[fast];
}
cout<<cnt;
}