演算法初探

內容:

  • APCS題解

  • APCS介紹

  • 演算法介紹

APCS題解

2021/11/07

掃過整個陣列,把0變成相鄰最低

(不會的話去複習語法

AC code

by kumokunn

#include<bits/stdc++.h>
using namespace std;

int main(){
 	int n;cin>>n;
 	int ans=0;
 	int arr[n];for(int i=0;i<n;i++)cin>>arr[i];
 	for(int i=0;i<n;i++){
 		if(arr[i]==0){
 			int tmp=101;
 			if(i>0)tmp=min(tmp,arr[i-1]);
			if(i<n-1)tmp=min(tmp,arr[i+1]);
			ans+=tmp;	
		}
	}
	cout<<ans<<endl;
	return 0;
}

煩躁實作題
紀錄每一格有沒有橫線、縱線

做好細節,閱讀不要燒雞

AC code

by yennnn

#include <iostream>
using namespace std;
int xline[101][101], yline[101][101];
/* 0: blank, -1: stake, 1 : line*/
/* xline: horizontal line, yline: vertical line*/
int main() {
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int m, n, h, mx = -1, cnt = 0;
    cin >> m >> n >> h;
    for (int i = 0; i <= m; i++) {
        fill(xline[i], xline[i] + n + 1, 0);
        fill(yline[i], yline[i] + n + 1, 0);
    }
    while (h--) {
        int r, c, t;
        cin >> r >> c >> t;
        if (t == 0) {
            int b = -1;
            if (xline[r][c] <= 0) {
                // not on the xline
                b = -1;
                // left
                for (int i = c - 1; i >= 0; i--) {
                    if (xline[r][i] == -1) {
                        b = i;
                        break;
                    }
                }
                if (b != -1) {
                    for (int i = b + 1; i < c; i++) {
                        xline[r][i] = 1;
                    }
                }

                b = -1;
                // right
                for (int i = c + 1; i < n; i++) {
                    if (xline[r][i] == -1) {
                        b = i;
                        break;
                    }
                }
                if (b != -1) {
                    for (int i = b - 1; i > c; i--) {
                        xline[r][i] = 1;
                    }
                }
            }

            if (yline[r][c] <= 0) {
                // not on the yline
                // up
                b = -1;
                for (int i = r - 1; i >= 0; i--) {
                    if (yline[i][c] == -1) {
                        b = i;
                        break;
                    }
                }
                if (b != -1) {
                    for (int i = b + 1; i < r; i++) {
                        yline[i][c] = 1;
                    }
                }

                b = -1;
                // down
                for (int i = r + 1; i < m; i++) {
                    if (yline[i][c] == -1) {
                        b = i;
                        break;
                    }
                }
                if (b != -1) {
                    for (int i = b - 1; i > r; i--) {
                        yline[i][c] = 1;
                    }
                }
            }
            xline[r][c] = -1;
            yline[r][c] = -1;

            cnt = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (xline[i][j] != 0 || yline[i][j] != 0) {
                        cnt++;
                    }
                }
            }
            mx = max(mx, cnt);
        } else {
            int b = -1;
            xline[r][c] = 0;
            yline[r][c] = 0;
            // up
            for (int i = r - 1; i >= 0; i--) {
                if (yline[i][c] == -1) {
                    b = i;
                    break;
                }
            }
            if (b != -1) {
                for (int i = b + 1; i < r; i++) {
                    yline[i][c] = 0;
                }
            }

            b = -1;
            // down
            for (int i = r + 1; i < m; i++) {
                if (yline[i][c] == -1) {
                    b = i;
                    break;
                }
            }
            if (b != -1) {
                for (int i = b - 1; i > r; i--) {
                    yline[i][c] = 0;
                }
            }

            b = -1;
            // left
            for (int i = c - 1; i >= 0; i--) {
                if (xline[r][i] == -1) {
                    b = i;
                    break;
                }
            }
            if (b != -1) {
                for (int i = b + 1; i < c; i++) {
                    xline[r][i] = 0;
                }
            }

            b = -1;
            // right
            for (int i = c + 1; i < n; i++) {
                if (xline[r][i] == -1) {
                    b = i;
                    break;
                }
            }
            if (b != -1) {
                for (int i = b - 1; i > c; i--) {
                    xline[r][i] = 0;
                }
            }

            cnt = 0;
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    if (xline[i][j] != 0 || yline[i][j] != 0) {
                        cnt++;
                    }
                }
            }
            mx = max(mx, cnt);
        }
    }
    cout << mx << '\n' << cnt << '\n';
    return 0;
}

目標:

  1. 知道每個位置的工作量

  2. 讓工作越快的機器在工作越多的位置

差分

\(a_i項的差分為a_{i}和a_{i-1}的差\)

令為\(b_i\)為\(a_i\)的差分

即\(b_i=a_{i}-a_{i-1}\)

性質:\(\sum_{k=1}^n b_i = a_i\)

差分

把區間\(2到5加上3\)

在差分相當於

在2加3

在6減3

1       2       3       4       5       6       7       8       9       10

+3

-3

算每個位置的工作量

就是將線段拆成在差分上的一個加一個減

最後再做前綴和

工作量多的位置

要放效率高的機器

若有兩個位置工作量\(a_i、a_j\)跟

兩個機器做一份工作花\(b_i、b_j\)時間

且\(a_i<a_j\),\(b_i<b_j\)

則有\(b_j*a_i+b_i*a_j>a_i*b_i+a_j*b_j\)

(移項後可證明

AC code(變數名稱不良示範

by kumokunn

#include<bits/stdc++.h>
#define ll long long int
using namespace std;
ll OAO[200002]={},QAQ[200001]={};
 int main(){
 	int n,m;cin>>n>>m;
 	while(m--){
 		int a,b,c;cin>>a>>b>>c;
 		OAO[a]+=c;
 		OAO[b+1]-=c;
	 }
	 for(int i=1;i<=n;i++){
	 	OAO[i]+=OAO[i-1];
	 }
	 for(int i=1;i<=n;i++){
	 	cin>>QAQ[i];
	 }
	 sort(OAO+1,OAO+n+1);
	 sort(QAQ+1,QAQ+n+1);
	 ll ans=0;
	 for(int i=1;i<=n;i++){
	 	ans+=QAQ[i]*OAO[n-i+1];
	 }
	 cout<<ans<<endl;
	return 0;
}


 

先備知識:二分圖判斷

方法1.BFS對點做奇偶判斷

方法2.DSU把邊視為點集的合併

奇偶判斷:畫顏色!

 

即相鄰節點奇偶不同(顏色不同

有相鄰節點奇偶不同則不合法

點集

一開始每個點都是一個點集

每一個邊要讓兩個點

和與對方相鄰的點在同一個點集

若有兩個相鄰點在同一點集則不合法

解法一、二分搜

每次考慮前綴的調查員和組長的資料

用BFS判斷是否為二分圖

合法調查員

不合法調查員

1             2            3             4            5              6

\(f(x)\):到x的前綴是否為二分圖

f(x) 1 1 1 0 0 0

解法二、DSU

分別用每個調查員的資料

對每個邊把點集和與對方相鄰的點集合併

若有相鄰點在同一點集則不合法

每次把DSU還原成只有組長資料的狀態

AC code(並查集

by  kumokunn

#include<bits/stdc++.h>
#define pb push_back
#define pf push_front
#define mp make_pair
#define F first
#define S second
#define pq priority_queue
#define ll long long int
#define pii pair<int,int>
#define endl '\n'
#define Orz ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define MAXN 100000
using namespace std;
int arr[20100],vis[20100]={};
vector<int> dis[20100],ans;
int find(int pos){
	if(arr[pos]!=pos){
		arr[pos]=find(arr[pos]);
		return arr[pos];
	}else return pos;
}
void unnion(int a,int b){
	if(find(a)==find(b))return; 
	if(find(a)>find(b))swap(a,b);
	arr[find(a)]=find(b);
	return;
}
void dfs(int pos,int pre){
	vis[pos]=1;
	for(auto i:dis[pos]){
		if(vis[i]==0){
			if(pre!=-1)unnion(pre,i);
			dfs(i,pos);
		}
	}
}
int main(){
 	int n,m;
 	cin>>n>>m;
 	for(int i=0;i<m;i++){
 		int a,b;cin>>a>>b;
 		dis[a].pb(b);
 		dis[b].pb(a);
	 }
	 for(int i=0;i<n;i++)arr[i]=i;
	 for(int i=0;i<n;i++){
	 	if(vis[i]==0){
	 		dfs(i,-1);
		}
	 }
	 int p,k;cin>>p>>k;
	 for(int j=0;j<p;j++){
	 	set<int> se;
	 	int flag=1;
	 	for(int i=0;i<k;i++){
	 		int a,b;cin>>a>>b;
	 		if(find(a)==find(b)){
	 			flag=0;
			}else{
			 	if(dis[a].size()>0){
			 		int tmp1=b,tmp2=dis[a][0];
					if(find(tmp1)>find(tmp2))swap(tmp1,tmp2);
			 		se.insert(find(tmp1));
			 		unnion(tmp1,tmp2);
				}
			 	if(dis[b].size()>0){
			 		int tmp1=a,tmp2=dis[b][0];
					if(find(tmp1)>find(tmp2))swap(tmp1,tmp2);
			 		se.insert(find(tmp1));
			 		unnion(tmp1,tmp2);
				 }
			 }
		 }
		 if(!flag){
		 	ans.pb(j+1);
		 	for(auto z:se)arr[z]=z;
		 } 
	 }
	 for(auto i:ans)cout<<i<<endl;
	return 0;
}


 

AC code(並查集

by  kennychenfs

#include <iostream>
#include <vector>
#define Hirasawa_Yui_My_Wife ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pb push_back
using namespace std;
const int N=2e4+10,P=1e4+10;
int n,m,p,k;
vector<int> G[N];
int color[N],lead[N],f[N],sz[N];//lead[i]=lead of color i
int oc(int c){return (c%2)?c+1:c-1;}
vector<int> ch;
int fa(int u){//zip = if 路徑壓縮
	if(f[u]==u)return u;
	ch.pb(u);
	return f[u]=fa(f[u]);
}
void dfs(int u,int c){
	color[u]=c;
	for(int i:G[u])if(!color[i])dfs(i,oc(c));
}
int main(){
	Hirasawa_Yui_My_Wife
	cin>>n>>m;
	int u,v;
	while(m--){
		cin>>u>>v;
		++u;++v;
		G[u].pb(v);
		G[v].pb(u);
	}
	int now=1;
	for(int i=1;i<=n;++i){
		if(!color[i])dfs(i,now),now+=2;
	}
	cin>>p>>k;
	for(int i=1;i<=now;++i)f[i]=i;
	vector<int> wrong;
	for(int o=1;o<=p;++o){
		now=0;
		bool flag=0;
		ch.clear();
		for(int i=0;i<k;++i){
			cin>>u>>v;
			++u;++v;
			if(flag)continue;
			int cu=color[u],cv=color[v];
			int x=fa(cu),y=fa(cv);
			if(x==y){wrong.pb(o);flag=1;continue;}
			f[x]=fa(oc(cv));
			f[y]=fa(oc(cu));
			ch.pb(x);
			ch.pb(y);
		}
		for(int i:ch)f[i]=i;
	}
	for(int i:wrong)cout<<i<<'\n';
}

AC code(二分搜

by  thanksone

#include <bits/stdc++.h>
#define pb push_back
#define mid (l + r) / 2
using namespace std;
struct e{
    int u, v;
};
int n;
array<bool, 10004> WA;
array<int, 20004> vis;
array<vector<e>, 10004> E;
array<vector<int>, 20004> G;
bool dfs(int u, int t){
    if(vis[u]) return 1;
    bool ans = 1;
    vis[u] = t;
    for(int v : G[u]){
        if(vis[v] == t) return 0;
        ans &= dfs(v, 3 - t);
    }
    return ans;
}
bool check(int p){
    bool ans = 1;
    for(int i = 0; i < n; i++){
        G[i].clear();
        vis[i] = 0;
    }
    for(int i = 0; i <= p; i++){
        for(auto [u, v] : E[i]){
            G[u].pb(v);
            G[v].pb(u);
        }
    }
    for(int i = 0; i < n; i++){
        ans &= dfs(i, 1);
    }
    return ans;
}
void BS(int l, int r){
    if(check(r)) return;
    while(l != r){
        if(check(mid)) l = mid + 1;
        else r = mid;
    }
    WA[l] = 1;
    E[l].clear();
}
signed main(){
    int m, p, k, a, b;
    cin >> n >> m;
    while(m--){
        cin >> a >> b;
        E[0].pb({a, b});
    }
    cin >> p >> k;
    for(int i = 1; i <= p; i++){
        for(int j = 0; j < k; j++){
            cin >> a >> b;
            E[i].pb({a, b});
        }
    }
    for(int i = 0; i < 3; i++){
        BS(0, p);
    }
    for(int i = 1; i <= p; i++){
        if(WA[i]) cout << i << "\n";
    }
    return 0;
}

APCS介紹

APCS為Advanced Placement Computer Science的英文縮寫

是指「大學程式設計先修檢測」

其檢測模式乃參考美國大學先修課程

(Advanced Placement,AP)

與各大學合作命題,並確定檢定用題目經過信效度考驗

以確保檢定結果之公信力

用途

  • 檢測能力
  • 升學
  • 取得TOI初選資格

APCS組

臺灣大學 3
清華大學 3
陽明交通大學 3
成功大學 3

準備策略

  • 熟悉語法(練習一些題目
  • 學習演算法

如何學習演算法

強強學長的slides

有了資源,還需要練習

學習演算法:

看講義or聽課

寫講義的題or找題寫

演算法介紹

演算法:解決問題的方法

資訊中的演算法,指的是可以經過

有限個步驟解決問題的一種方法

演算法也可以是一種邏輯思維

栗子

問題:把蘋果做成蘋果汁

  1. 清洗蘋果
  2. 將蘋果削皮、去籽
  3. 將經過步驟(2.)處理的蘋果放入果汁機
  4. 在果汁機中加入一定比例的水
  5. 按下果汁機啟動按鈕
  6. 將果汁機裡面的蘋果汁倒入玻璃杯中
演算法:

四種常見演算法的思維

  1. 枚舉

  2. 貪婪(greedy

  3. 動態規劃(DP

  4. 分治(DC

枚舉

把候選解都試過一遍

在數列中有沒有兩個數合為10

1,2,3,4,5,6,7,8,9,10

每次抓兩個數,看相加和是不是10

1+2,1+3,1+4,1+5.....2+3,2+4...

栗子

貪婪

不拿白不拿

栗子

吃慢得先吃一定比較好

動態規劃

做過的子問題就不要再做一次

栗子

利用紀錄來避免重複計算子問題

費撥那契數列第N項

分治

把一個問題分成幾個小問題

枚舉

有可能的都試一遍

例題

給你一個數列\(a_1,a_2...a_n\)
是否有兩個數和為\(K\)

 

用兩個for枚舉

greedy

不拿白不拿

\(\rightarrow\)照這個策略一定是最優解(之一)

例題

greedy策略

吃越慢的越早吃

真的對嗎?

簡單證明A部分:

因為老闆會一直煮菜

不管順序​如何,最後一道煮完的時間固定

簡單證明B部分:

若有兩個人(A紅色、B藍色)

的吃飯時間如下
 

交換後會有更好的解

DP

曾經有人說過

不能greedy就DP

特性

重複子問題

相同的一個子問題,需要多次查詢


最佳子結構

可以透過這些子問題得到這個問題的最佳解

 

無後效性

已經完成的子問題不會因後續的子問題

而非最佳解

實作方法

top_down(迴圈)                                  bottom_up(函數)

吠事數列

扣的

int main(){
	int arr[48763];
	arr[0]=1,arr[1]=1;
	for(int i=2;i<=10;i++)arr[i]=arr[i-1]+arr[i-2];
	return 0 ;
} 

爬個樓梯

justforfun時常有腿太長的困擾

爬樓梯常常要決定一次要爬幾階(1~3階)

給你他要爬幾階樓梯,請問他有幾種爬法

DP步驟

  1. 設定狀態:走到這一階的方法數
  2. 設定轉移式:\(a_i=a_{i-1}+a_{i-2}+a_{i-3}\)
  3. 設定基礎:\(a_0=1,a_1=1,a_2=2\)

扣的

int main(){
	int arr[48763];
   	int n;cin>>n;
	arr[0]=1,arr[1]=1;arr[2]=2;
	for(int i=3;i<=n;i++)arr[i]=arr[i-1]+arr[i-2]+arr[i-3];
    	cout<<arr[n]<<endl;
	return 0 ;
} 

小練習

DP其實有很多很多經典題

應該都會講ㄅ(?

  • 背包
  • 區間DP
  • 最大子陣列和
  • LIS 
  • edit distance
  • 切正方形

分治

by yennnn

演算法初探

By linki1010111

演算法初探

  • 768