C++是許多語言的遠房親戚甚至老祖宗
許多學過C++的人都表示由C++跳至其他語言非常容易
因為C++剛好兼具了許多語言的風格
例如:
但C++本身說難不難,說簡單也可以不簡單
不過呢~這堂課主要是訓練大家的程式邏輯、運算思維
C++只是作為一個速度夠快的結構化語言
讓大家學習對電腦來說怎樣是清楚、有效率的步驟
#include <iostream>
#include <algorithm>
using namespace std;
long long dp[100000], k, n, w, v;
int main() {
cin >> n >> k;
for(int i = 0; i < n; i++) {
cin >> w >> v;
for(int W = k; W >= w; W--) dp[W] = max(dp[W], dp[W-w] + v);
}
cout << dp[k] << '\n';
}
大家都知道電腦擁有超快速且準確的運算能力
實際上普通的電腦一秒大概可以執行\(10^9\)次的運算
但是呢~電腦執行的指令依舊是人們下達的
所以我們如果下錯指令,可能會無法達到預期的效果
一個演算法首先要有明確的步驟
並且保證能在有限的時間內結束
在做事的時候心中已有了個完整的流程圖
演算法的課程即是訓練此種邏輯思維
分析演算法們的正確性及效率會用到一些數學
不過講師講的一定都可以讓大家聽懂的啦!
而且有許多國中、小可能就學過的東西
最大公因數
試除法
質因數分解法
輾轉相除法
短除法
試除法
質因數分解法
輾轉相除法
短除法
\(O(N)\)
\( \rightarrow O(\sqrt{N})\)
試除法
質因數分解法
輾轉相除法
短除法
\(O(N)\)
\( \rightarrow O(\sqrt{N})\)
\( O(\sqrt{N})\)
試除法
質因數分解法
輾轉相除法
短除法
\(O(N)\)
\( \rightarrow O(\sqrt{N})\)
\( O(\sqrt{N})\)
\( O(\sqrt{N})\)
試除法
質因數分解法
輾轉相除法
短除法
\(O(N)\)
\( \rightarrow O(\sqrt{N})\)
\( O(\sqrt{N})\)
\( O(\sqrt{N})\)
\( O(\log N)\)
利用各種語法和演算法來解決各式各樣的題目!
Green Judge
TIOJ Infor Online Judge
Codeforces
AtCoder
Zero Judge
......
HP codewars
能力競賽
NPSC
TOI / IOI
APCS(?
教電腦說話
#include<iostream>//引入C++ 輸出輸入的函式庫
using namespace std;//不知道
int main()//主函式 電腦會由上到下執行其中的內容
{
cout << "Hello world!" << endl;//輸出的啦
}
#include<iostream>
using namespace std;
int main()
{
cout << "^_^" << endl;
cout << ""o"" << endl;
cout << "\^o^/" << endl;
}
What's the matter?
cout << ""o"" << endl;
cout << "\"o\"" << endl;
What's the matter?
cout << "\^o^/" << endl;
cout << "\\^o^/" << endl;
#include<iostream>
using namespace std;
int main()
{
cout << "^_^" << endl;
cout << "\"o\"" << endl;
cout << "\\^o^/" << endl;
}
拿來裝值的容器
#include<iostream>
using namespace std;
int main()
{
int a = 5,b = 10;
string s = "Hi!";
float f = 3.614;
cout << a << " " << b << endl;
cout << s << endl;
cout << f << endl;
a = 1;
b = 11;
cout << a << " " << b << endl;
}
跟電腦說話
#include<iostream>
using namespace std;
int main()
{
int a,b,c;
cin >> a >> b >> c;
cout << a + b + c << endl;
}
就是加減乘除(還有趴)
#include<iostream>
using namespace std;
int main()
{
int a = 5,b = 10;
cout << "a + b = " << a + b << endl;
cout << "a - b = " << a - b << endl;
cout << "a * b = " << a * b << endl;
cout << "a / b = " << a / b << endl;
cout << "a % b = " << a % b << endl;
double s = 1.05,t = 0.01;
cout << "s + t = " << s + t << endl;
cout << "s - t = " << s - t << endl;
cout << "s * t = " << s * t << endl;
cout << "s / t = " << s / t << endl;
}
#include<iostream>
using namespace std;
int main()
{
int a,b,c;
cin >> a >> b >> c;
if(a >= b && b >= c)
{
cout << a << endl;
}
else if(b >= c)
{
cout << b << endl;
}
else
{
cout << c << endl;
}
}
#include<iostream>
using namespace std;
int main()
{
int a,b,c;
cin >> a >> b >> c;
if(a >= b && a >= c)
cout << a << endl;
else if(b >= c)
cout << b << endl;
else
cout << c << endl;
}
#include<iostream>
using namespace std;
int main()
{
int i = 0;
while(i < 10)
{
cout << i <<endl;
i++;
}
}
#include<iostream>
using namespace std;
int main()
{
int i;
for(i = 0;i < 10;i++)//用要分號隔開喔!
{
cout << i << endl;
}
}
輸入一個正整數n,然後輸入n個數字(\(n\leq 5\))
把這n個數字的順序顛倒,然後輸出
呃...開n個變數?
#include <iostream>
using namespace std;
int main() {
int n;
int a,b,c,d,e;
cin >> n;
if(n == 1) {
cin >> a;
cout << a << endl;
}else if(n == 2) {
cin >> a >> b;
cout << b << ' ' << a << endl;
}else if(n == 3) {
cin >> a >> b >> c;
cout << c << ' ' << b << ' ' << a << endl;
}else if(n == 4) {
cin >> a >> b >> c >> d;
cout << d << ' ' << c << ' ' << b << ' ' << a << endl;
}else if(n == 5) {
cin >> a >> b >> c >> d >> e;
cout << e << d << ' ' << c << ' ' << b << ' ' << a << endl;
}
}
當一大堆變數的用途都差不多,我們可以為他們一起開一個空間,宣告時共用一個名字,用數字代表是哪個變數
分辨不同格子的編號稱為索引。注意!陣列的索引值是從 0 開始編號,稱為 \(\textbf{0-base}\)。也就是說:
索引值為0時,代表第1個變數
索引值為1時,代表第2個變數
索引值為2時,代表第3個變數
宣告的語法是 \(\texttt{int arr[Size];}\)
常常會搭配for迴圈使用
#include <iostream>
using namespace std;
int main() {
int n,i,a[5];
cin >> n;
// 合法的索引是0,1,...n-2,n-1,因此我們常用以下這種迴圈
for(i = 0; i < n; i++)
cin >> a[i];
for(i = n-1; i >= 0; i--)
cout << a[i] << ' ';
cout << endl;
}
sort(A+a,A+b+1);
#include<algorithm>
#include<iostream>
#include<algorithm>//別忘記!
using namespace std;
int main()
{
int N,i;
int A[100009];//宣告陣列
cin >> N;
for(i = 0;i < N;i++)
{
cin >> A[i];
}
sort(A,A+N);
for(i = 0;i < N;i++)
{
cout << A[i] << " ";
}
cout << endl;
}
#include<iostream>
#include<algorithm>
using namespace std;
int A[1009],S[1009];//宣告陣列(初始值為零)
int main()
{
int N,i;
cin >> N;
for(i = 1;i <= N;i++)
{
cin >> A[i];
S[i] = S[i-1] + A[i];
}
for(i = 1;i <= N;i++)
{
cout << S[i] << " ";
}
cout << endl;
}
#include<iostream>
#include<algorithm>
using namespace std;
int A[1009];//宣告陣列(初始值為零)
int main()
{
int N,i,k,ans = 0,j;
cin >> N;
for(i = 1;i <= N;i++)
{
cin >> A[i];
}
cin >> k;
for(i = 1;i <= N;i++)
{
for(j = i + 1;j <= N;j++)
{
if(A[i] +A[j] == k)
{
ans++;
}
}
}
cout << ans << endl;
}
*每個數字不超過C
#include<bits/stdc++.h>
using namespace std;
int cnt[100009];
int main()
{
int N,i,j,k,Q;
cin >> N;
for(i = 0;i < N;i++)
{
cin >> k;
cnt[k] = 1;
}
cin >> Q;
for(i = 0;i < Q;i++)
{
cin >> k;
if(cnt[k] == 1)
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
}
每次把區間劈成兩半
#include <iostream>
int main() {
int N, Q, A[100000], i, x;
cin >> N >> Q;
for(i = 0;i < N;i++) cin >> A[i];
sort(A,A+N);
for(i = 0;i < Q;i++) {
cin >> x;
int L = 0, R = N-1, ok = 0;
while(L <= R) {
int mid = (L+R)/2;
if(A[mid] < x)
L = mid+1;
else if(A[mid] >= x)
R = mid;
}
if(A[L] == x) cout << "Yes" << endl;
else cout << "No" << endl;
}
}
給一個序列 [a1,a2,...,an],詢問 q 個 x
回答至少要選幾個數字,總和才會不小於 x
每次看「選了 mid 個數字總和有沒有超過 x」
如果有就 r = mid (表示至多mid就足夠了)
否則 l = mid+1 (表示要比mid多才夠)
這種技巧稱為對答案二分搜,通常是題目必須求最小次數等情況使用,注意必須有單調性才能使用二分的技巧喔!
不一定要寫的麻煩例題
若拿一個物體的\( \frac{a}{b}(a \leq b)\),則其重量為\(w_i \times \frac{a}{b}\),獲得價值為\(v_i \times \frac{a}{b}\)
character / string
C++ 中用單引號代表字元
型別是 char (但是內部儲存還是數字)
#include <iostream>
using namespace std;
int main() {
char c = 'a';
cout << c << endl;
int i = 'z';
cout << i << endl;
}
雙引號代表的則是字串!
我們可以用string型別來儲存字串
#include <iostream>
using namespace std;
int main() {
string s = "J12Z";
cout << s << endl;
cin >> s;
cout << "GZ " << s << endl;
}
字串的本質是字元的陣列
所以它有長度,可以用方括號存取第i個字元是什麼
(注意不要讓[]裡面的i>=字串的length()!)
#include <iostream>
using namespace std;
int main() {
string s = "J12Z";
cout << s.length() << endl;
cout << s[0] << 'I' << s[3] << s[3] << endl;
// s[4] 超出陣列外,有機會 RE !
}
#include <iostream>
using namespace std;
int main()
{
long long above = 1, below = 1, n, r, i;
cin >> n >> r;
for(i = 1; i <= n; i++)
{
above *= i;
}
for(i = 1; i <= r; i++)
{
below *= i;
}
for(i = 1; i <= n-r; i++)
{
below *= i;
}
cout << above/below << '\n';
}
Do not Repeat Yourself
#include <iostream>
using namespace std;
long long fac(int n) { // return n!
long long ans = 1, i;
for(i = 1; i <= n; i++) {
ans *= i;
}
return ans;
}
int main() {
int n,r;
cin >> n >> r;
cout << fac(n)/(fac(r)*(fac(n-r))) << '\n';
}
#include <iostream>
using namespace std;
long long fac(int n) { // return n!
long long ans = 1, i;
for(i = 1; i <= n; i++) {
ans *= i;
}
return ans;
}
long long C(int n,int r) { // return C_r^n
return fac(n) / (fac(r)*fac(n-r));
}
int main() {
int n,r;
cin >> n >> r;
cout << C(n,r) << '\n';
}
long long fac(int n) { // return n!
long long ans = 1, i;
for(i = 1; i <= n; i++) {
ans *= i;
}
return ans;
}
回傳型別(你回傳的東西是什麼型態)
參數(你傳了什麼東西進來)
return的時候就會結束這個函數,回傳該回傳的東西給呼叫他的地方
中間一樣可以用迴圈、if、else等等
void pyramid(int n = 5) {
int i,j;
for(i = 1; i <= n; i++) {
for(j = 1; j <= i; j++) {
cout << '*';
}
cout << '\n';
}
}
int main() {
pyramid();
pyramid(3);
pyramid(10);
}
甚至呼叫自己(稱為遞迴)
不過下次再講(?
int f(int x)
{
return f(x-1) + f(x-2);
}
int f(int x)
{
if(x == 1 || x == 2) return 1;
return f(x-1) + f(x-2);
}
int gcd(int a,int b)
{
if(b == 0) return a;
return gcd(b,a%b);
}
縮排簡單來說就是在結構化指令的區塊加上前綴的tab鍵或空白以便閱讀
下面這段程式碼真是醜的極致,完全沒有縮排,很難看到哪個if對到哪個else或大括號怎麼包含的
#include <iostream>
using namespace std;
int main() {
int i,j;
cin>>i>>j;
if(i==0){
if(j%2==1){
cout<<i+j<<endl;
}else{
cout<<i-j<<endl;
}
}
else{
cout<<"NO"<<endl;
}
}
舒服多了! 這裡用的是tab縮排,程式的結構一目了然
#include <iostream>
using namespace std;
int main() {
int i,j;
cin >> i >> j;
if(i == 0) {
if(j%2 == 1) {
cout << i+j << endl;
}else {
cout << i-j << endl;
}
}else {
cout << "NO" << endl;
}
}
有些語言中必須要縮排才是正確的語法,例如python、YAML、Haskell等
C++並沒有硬性規定要如何縮排,但是縮排能夠幫助程式碼的閱讀,不論是自己之後看或是給別人理解都非常有助益。
有時常常會因為變數只取一個英文字母導致自己不知道他是做什麼用的,為了這個加上的註解都是「笨註解」
在變數命名時可以盡量考量這個變數的作用,例如:年分可以命名為year, yy, Y,或者線段可以叫做segment, seg, S。
取用長的命名或短的,原則上是範圍越大的變數,命名要越長、越清晰;反之則可以越短越重複,例如單個迴圈的計數變數我們幾乎都會命名為 i,而若在全域變數出現了t,u,v,我們幾乎無法想像那是什麼用途的變數。
// dreamoon_love_AA的code,醜到slides都不知道這是C++(X
#include<iostream>
#define CASET int ___T; scanf("%d", &___T); for(int cs=1;cs<=___T;cs++)
#define REP(I, N) for (int I = 0; I < (N); ++I)
void R(int &x) { scanf("%d", &x); }
void W(const int &x) { printf("%d", x); }
int main(){
CASET{
int AA[5];
REP(i,5)R(AA[i]);
if((AA[0]+AA[2]-1)/AA[2]+(AA[1]+AA[3]-1)/AA[3]>AA[4])W(-1);
else W((AA[0]+AA[2]-1)/AA[2],(AA[1]+AA[3]-1)/AA[3]);
}
return 0;
}
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T-- > 0) {
int A, B, C, D, K;
cin >> A >> B >> C >> D >> K;
bool success = false;
for (int X = 0; X <= K; X++) {
int Y = K - X;
if (X * C >= A && Y * D >= B) {
success = true;
cout << X << ' ' << Y << '\n';
break;
}
}
if (!success)
cout << -1 << '\n';
}
}
#include <iostream>
using namespace std;
int main() {
int n,i,a[5];
cin >> n;
for(i = 0; i < n; i++) {
cin >> a[i];
}
for(i = n-1; i >= 0; i--) {
cout << a[i] << ' ';
}
cout << endl;
}
#include <iostream>
using namespace std;
int main()
{
int n,i,a[5];
cin >> n;
for(i = 0; i < n; i++)
{
cin >> a[i];
}
for(i = n-1; i >= 0; i--)
{
cout << a[i] << ' ';
}
cout << endl;
}
兩種style:換行or不換行(p.s.有一個講師最不喜歡大括號,能不加就不加)
#include < iostream >
using namespace std;
int main() {
int n, i, a [ 5 ];
cin >> n;
for( i = 0; i < n; i ++ ) {
cin >> a [ i ];
}
for( i = n - 1; i >= 0; i -- ) {
cout << a [ i ] << ' ';
}
cout << endl;
}
#include<iostream>
using namespace std;
int main(){
int n,i,a[5];
cin>>n;
for(i=0;i<n;i++){
cin>>a[i];
}
for(i=n-1;i>=0;i--){
cout<<a[i]<<' ';
}
cout<<endl;
}
空格有些人喜歡加爆,有些人死都不加
#include <iostream>
using namespace std;
// 下面這段程式把一段數字倒過來輸出
int main() {
int n,i,a[5];
cin >> n;
for(i = 0; i < n; i++) {
cin >> a[i];
}
for(i = n-1; i >= 0; i--) {
cout << a[i] << ' ';
}
cout << endl;
}
通常用大綱就好,除非是函式的使用方法或注意事項等需要多一些說明的東西
註解每一行的作用很製杖(
有個 n 階段的階梯,一次可跨上 1 或 2 個階段,
問有多少種不同的方式,踏上第 n 階段。
\(dp[n] = dp[n-1] + dp[n-2]\)
\(dp[0] = dp[1] = 1\)
有個 n 階段的階梯,一次可跨上 1 或 2 個階段,
並且每一階段都有一個事件,可能會讓你增加或減少金錢
問走到第 n 階的過程中最多能獲益多少?
\(dp[n] = \max(dp[n-1] + dp[n-2]) + val_n\)
\(dp[0] = 0\)
\(dp[1] = val_1\)
int dp[1000];
int main() {
int i,n;
cin >> n;
dp[0] = dp[1] = 1;
for(i = 2; i <= n; i++) dp[i] = dp[i-1]+dp[i-2];
cout << dp[n] << '\n';
}
int dp[1000];
int DP(int n) {
if(dp[n]) return dp[n];
dp[n] = DP(n-1)+DP(n-2);
return dp[n];
}
int main() {
int i,n;
cin >> n;
dp[0] = dp[1] = 1;
cout << DP(n) << '\n';
}
int dp[1000],val[1000];
int main() {
int i,n;
cin >> n;
for(i = 1; i <= n; i++) cin >> val[i];
dp[0] = 0;
dp[1] = val[1];
for(i = 2; i <= n; i++) dp[i] = max(dp[i-1]+dp[i-2]) + val[i];
cout << dp[n] << '\n';
}
int dp[1000],val[1000],calced[1000];
int DP(int n) {
if(calced[n]) return dp[n];
dp[n] = max(DP(n-1)+DP(n-2)) + val[n];
calced[n] = 1;
return dp[n];
}
int main() {
int i,n;
cin >> n;
for(i = 1; i <= n; i++) cin >> val[i];
dp[0] = 0;
dp[1] = val[1];
calced[0] = calced[1] = 1;
cout << DP(n) << '\n';
}
給一個長度 n 的序列
問裡面一個連續的區間總和最多是多少?(不能不拿)
\(dp[i] = \max(dp[i-1], 0) + val_i\)
\(dp[0] = 0\)
#include <iostream>
using namespace std;
int main() {
int n,i;
cin >> n;
for(i = 1; i <= n; i++)
cout << i << ". I don't say swear words!" << endl;
}
cin.getline(str) getline(cin, str) getline(cin, str, delim)
#include <iostream>
using namespace std;
int main() {
string str;
while(1) {
getline(cin, str);
if(str == "END") break;
int i, last = 0;
cout << char(str[0]-'a'+'A');
for(i = 1; i < str.size(); i++) {
if(str[i-1] == ' ') {
cout << char(str[i]-'a'+'A');
last = i;
}
}
cout << ' ';
for(i = last; i < str.size(); i++)
cout << str[i];
cout << endl;
}
}
#include <iostream>
#include <sstream>
using namespace std;
int main() {
string str;
while(1) {
getline(cin, str);
if(str == "END") break;
stringstream ssin(str);
while(ssin >> str) {
cout << char(str[0]-'a'+'A');
}
cout << ' ' << str << endl;
}
}
#include <iostream>
using namespace std;
int main() {
int a1, an, d, i;
cin >> a1 >> an >> d;
for(i = a1; i != an; i += d) {
cout << i << ' ';
}
cout << an << endl;
}
需要注意的事情
首先想到一個\(\mathcal{O}(nc)\)的解
從小到大檢查一個半徑可不可行
把座標從左到右排序
可以知道最左邊的基地台一定越右邊越好
但是必須包含最左邊的服務點
可以發現! 如果一個半徑r可行
那比r大的半徑都可行
二分搜半徑!
int l = 0, r = 100000000; // [l,r]
while(l < r) {
int mid = l+(r-l)/2;
if(ok(mid))
l = mid;
else
r = mid-1;
}
cout << l << endl;
為了預防大家不知道怎麼二分搜這邊放了大概的框架
只要把ok這個檢查用的函數寫好就好
???
不知道做什麼就來做題目ㄅ
#include<vector>//記得!
#include<iostream>
int main(){
vector<int> vec;//宣告
for(int i=0 ; i<5 ; i++){
vec.push_back(i * 10); // [0, 10, 20, 30, 40]
}
vec.pop_back(); // 移除 40
vec.pop_back(); // 移除 30
for(int i=0 ; i<vec.size() ; i++){ // vec.size() = 3
cout << vec[i] << endl; // 輸出 0, 10, 20
}
cout << vec.front() << " " << vec.back() << endl;
}
#include <set>//記得
#include<iostream>
using namespace std;
int main(){
set<int> mySet;
mySet.clear();
mySet.insert(20); // mySet = {20}
mySet.insert(10); // mySet = {10, 20}
mySet.insert(30); // mySet = {10, 20, 30}
cout << mySet.count(20) << endl; // 存在 -> 1
cout << mySet.count(100) << endl; // 不存在 -> 0
mySet.erase(20); // mySet = {10, 30}
cout << mySet.count(20) << endl; // 0
for(auto t : mySet){
cout << t << " "; //{10, 30}
}
cout << endl;
}
#include <map>
#include <iostream>
using namespace std;
int main(){
map<string, int> m; // 從 string 對應到 int
m["one"] = 1; // "one" -> 1
m["two"] = 2; // "two" -> 2
m["three"] = 3; // "three" -> 3
cout << m["one"] << endl; // 1
cout << m["three"] << endl; // 3
cout << m["ten"] << endl; // 0 (無對應值)
}
這邊的名詞不同地方可能會有小差異,不要太在意XD
不要偷看答案(#
最常用來儲存圖的方法有兩種
https://tioj.ck.tp.edu.tw/problems/1831
(不要排斥TIOJ喔QQ)
#include <iostream>
#include <algorithm>
#include <vector>
#define sort_uni(v) sort(begin(v),end(v)), v.erase(unique(begin(v),end(v)),end(v))
using namespace std;
const int N = 501;
int n,m,k;
vector<int> adjL[N]; // 鄰接串列(?)
bool adjM[N][N]; // 鄰接矩陣
string s;
void addEdge(int a,int b){
adjM[a][b] = true;
adjL[a].push_back(b);
}
signed main(){
cin >> n >> m >> k;
for(int i = 0, a, b; i < m; i++) {
cin >> a >> s >> b;
if(s.front() == '<') addEdge(b,a);
if(s.back() == '>') addEdge(a,b);
}
if(k) {
for(int i = 1; i <= n; i++) {
cout << i << " ->";
if(adjL[i].size() == 0)
cout << " 0\n";
else {
sort(adjL[i].begin(), adjL[i].end());
for(int j = 0; j < adjL[i].size(); j++) {
if(j == 0 || adjL[i][j] != adjK[i][j-1])
cout << ' ' << adjL[i][j];
}
cout << '\n';
}
}
}else {
for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++)
cout << adjM[i][j] << " \n"[j==n];
}
}
如果忘記\(\verb`vector`\)或function的語法可以翻前面!
沒教到的話錯在我身上(掩面
走到一個頂點之後,會試圖往下一個頂點走,不得已才會往回走,能走得距離起點越深越好
怎麼實作呢?C++中直接利用函數遞迴正好符合這樣的要求,另一個方法是利用 \(\verb`stack`\)
vector<int> adj[N];
bool vis[N];
void dfs(int u) {
if(vis[u]) return; // 已經走過就不要再走了,以免繞圈圈陷入無窮迴圈
vis[u] = true;
cout << "now at " << v << '\n';
for(int v: adj[u])
dfs(v); // 遞迴呼叫dfs,在回到u以前嘗試走所有的v
}
走到一個頂點之後,會試圖往下一個頂點走,不得已才會往回走,能走得距離起點越深越好
怎麼實作呢?C++中直接利用函數遞迴正好符合這樣的要求,另一個方法是利用 \(\verb`stack`\)
vector<int> adj[N];
bool vis[N];
void dfs(int u) {
stack<int> s;
vis[s] = true;
s.push(u);
while(!s.empty()) {
int now = s.top(); s.pop();
cout << "now at " << now << endl;
for(int nxt: adj[now]) {
if(!vis[nxt]) {
vis[nxt] = true;
s.push(nxt);
}
}
}
}
走到一個頂點之後,先看過所有鄰居,不得已才會走距離更遠的地方,走得越廣越好,可以保證走到每一個頂點的時候都是走最短的路徑
通常利用 \(\verb`queue`\)實作。什麼?那是什麼?\(\verb`queue`\)是一個先進先出的資料結構,它就像是排隊的顧客,先進來的顧客會先處理,包含在C++標準函式庫中
因此,我們每遇到一個頂點,我們就將所有沒造訪過的鄰居丟進\(\verb`queue`\),由於距離起點比較近的點一定會位在\(\verb`queue`\)比較前面的位置,我們可以保證距離比較近的會先被處理
vector<int> adj[N];
bool vis[N];
int dis[N];
void dfs(int start) {
queue<int> q;
vis[start] = true;
dis[start] = 0;
q.push(start); // 把「檢查start的鄰居!」這件事丟進queue
while(!q.empty()) {
int now = q.front(); q.pop(); // 看現在應該先檢查誰的鄰居
for(int nxt: adj[now]) {
if(!vis[nxt]) { // 如果檢查到沒有造訪過的鄰居就丟進queue裡面,之後再看他的鄰居
vis[nxt] = true;
dis[nxt] = dis[now]+1;
q.push(nxt);
}
}
}
}
如果真的不知道怎麼寫就來看code吧><不要照抄喔!
TIOJ 1369 https://pastebin.com/DFyE1UPu
TIOJ 1022 https://pastebin.com/MtbknaC6