程序設計思維 複習大綱

1 程序設計基本常識

1.1 關閉cin,cout 同步

使用後不能再使用scanf || printf

ios::sync_with_stdio(false);

1.2 快速讀入

void read(int &x) {
    x = 0;
    int f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x *= 10;
        x += (int) (ch - '0');
        ch = getchar();
    }
    x *= f;
}

1.3 帶模快速冪

typedef long long LL;
LL ksm(LL a, LL p, LL k) {
    LL ans = 1;
    for (; p; p >>= 1, a = a * a % k)
        if (p & 1)
            ans = ans * a % k;
    return ans;
}

1.4 帶模快速乘

typedef long long LL;
LL ksc(LL a, LL b, LL k) { // a*b%k
    LL ans = 0;
    while (b) { // 當 b 不爲 0 時繼續循環
        if (b & 1) ans = (ans + a) % k;
        a = (a + a) % k;
        b >>= 1;
    }
    return ans;
}

2 C++ 與 STL

2.1 輸入輸出

2.1.1 格式串

  • %1d 只讀1個number char
  • %02d 輸出2位整數不足前導補零
  • %d:%d-%d:%d 格式化讀入
  • %.2f 保留兩位小數
  • %o 八進制
  • %x %#x %#X 十六進制
  • #[^\n] 搭配 getchar() 可讀入一行 (?)

2.1.2 讀入一行 & 字符串相關

  • printf("%s", stringObject.c_str()); 輸出C++ string 轉化成 char
  • gets(aline);
  • sscanf("123456 ", "%s", str); sscanf與scanf類似,都是用於輸入的,只是後者以屏幕(stdin)爲輸入源,前者以固定字符串爲輸入源。
  • sprintf(s, "%d", 123); 由於sprintf 跟printf 在用法上幾乎一樣,只是打印的目的地不同而已,前者打印到字符串中,後者則直接在命令行上輸出。這也導致sprintf 比printf 有用得多。

2.2 vector

在這裏插入圖片描述

2.3 stack

在這裏插入圖片描述

2.4 queue & priority_queue

在這裏插入圖片描述

2.5 map

在這裏插入圖片描述
在這裏插入圖片描述

2.6 set

在這裏插入圖片描述

2.7 unordered_map/set

在這裏插入圖片描述

2.8 algorithm sort

在這裏插入圖片描述

3 搜索

3.1 全排列 old

void print() {
    for (int i = 1; i <= n; ++i)
        printf("%d ", num[i]);
    cout << '\n';
}
void dfs(int x) {
    if (x > n) {
        print();
        return;
    }
    for (int i = 1; i <= n; ++i) {
        if (!vis[i]) {
            num[x] = i;
            vis[i] = 1;
            dfs(x + 1);
            vis[i] = 0;
        }
    }
}

3.2 半排列 old

void print() {
    for (int i = 1; i <= k; ++i)
        printf("%d ", ans[i]);
    cout << '\n';
}
void dfs(int x, int last) {
    if (x > k) {
        print();
        return;
    }
    for (int i = last + 1; i <= n; ++i) {
        ans[x] = num[i];
        dfs(x + 1, i);
    }
}

3.3 dfs子集枚舉 old

void print() {
    for (int i = 1; i <= n; ++i)
        printf("%d ", vis[i]);
    cout << '\n';
}
void dfs(int x) {
    if (x > n) {
        print();
        return;
    }
    vis[x] = 0;
    dfs(x + 1);
    vis[x] = 1;
    dfs(x + 1);
    vis[x] = 0;
}

3.4 二進制子集枚舉

    int n; cin >> n;
    for (int s = 0; s < (1 << n); ++s) {
        for (int j = n - 1; j >= 0; --j)
            cout << (bool) (s & (1 << j)) << " ";
        cout << '\n';
    }

5 二分

5.1 lower_bound & upper_bound

lower_bound是找到第一個≥x的位置,upper_bound是找到第一個>x的位置。

5.2 理解和寫法

二分邊界問題:即l,r分別用來逼近答案和縮小區間。在這個題中,如果不滿足條件,那麼就l=mid+1縮小區間,滿足的話r=mid來逼近。最終符合條件的是mid 或 r特別注意的一點:這時計算mid的時候要l + (r-l+1)/2否則會因爲mid一直==l,而又check==1所以死循環。
類似的二分還可以有另外一種寫法:用l逼近區間,r來縮小區間,但是這時候注意是左閉右開的,就是那mid正常寫就可了。然後結果l-1的原因:觀察最後小區間的狀態可以得知:當我們檢查到midmid是中位數時,因爲mid前面的嚴格小於的元素個數是小於mid所在的位置的,所以會執行l = mid+1,這一句話,所以最終的狀態是 l-1 爲最後我們檢查合法的元素。

5.3 栗子

		while (l < r) {
			mid = l + (r-l)/2;
			// cout << l << ", " << r << "mid = " << mid << endl;
			// cout << "now mid = " << mid << endl;
			if (check(mid)) { // 如果這個數前面數字達不到中位數個數的情況// 前面如果有小於等於 nums 個數。這個數一定不行
				l = mid+1;
			} else {// 這個數字可能彳亍
				r = mid;
			}
		}
		printf("%d\n",l-1);
		while (l < r) {
			mid = l + (r-l+1)/2;
			// cout << l << ", " << mid << ", " << r << endl;
			// cout << "now mid = " << mid << endl;
			// 嚴格小於的個數小於真正中位數的位置
			if (check(mid)) { // 如果這個數前面數字達不到中位數個數的情況// 前面如果有小於等於 nums 個數。這個數一定不行
				l = mid;
			} else {// 這個數字可能彳亍
				r = mid-1;
			}
		}
		// l-1的原因:觀察最後小區間的狀態可以得知:當我們檢查到mid,mid是中位數時,因爲mid前面的嚴格小於的元素個數是小於mid所在的位置的,
		// 所以會執行 l = mid+1,這一句話,所以最終的狀態是 l-1 爲最後我們檢查合法的元素。
		printf("%d\n",l);

6 線性數據結構

6.1 單調棧

在這裏插入圖片描述

6.2 單調隊列

在這裏插入圖片描述

7 圖與樹

7.1 存圖鄰接表

memset(first,-1,sizeof(first));
void build(int ff, int tt, int dd) {
	es[++tot] = (edge){ff,tt,dd};
	nxt[tot] = first[ff];
	first[ff] = tot;
}

7.2 樹的直徑

兩遍dfs

7.3 並查集

int find(int x) {
	int t, r = x;
	while(r != fa[r]) r = fa[r];
	while(x != r) {t=fa[x];fa[x]=r;x=t;}
	return r;
}

void lianjie(int x, int y) {
	int fx = find(x), fy = find(y);
	if(fx != fy) fa[fx] = fy;
}

void check(int x, int y) {
	int fx = find(x), fy = find(y);
	if(fx != fy) puts("N");	
	else puts("Y");
}

7.4 最小生成樹

int kruskal() {
	int ans = 0;
	for(int i = 1; i <= V; ++ i) fa[i] = i;
	sort(es+1, es+E+1, cmp);
	for(int i = 1; i <= E; ++ i) {
		int fu = find(es[i].from);
		int fv = find(es[i].to);
		if(fu != fv) {
			fa[fu] = fv;
			ans += es[i].cost;
		}
	}
	return ans;
}

7.5 多源最短路 - Floyd

void floyd() {
	for(int k = 1; k <= V; ++ k)
		for(int i = 1; i <= V; ++ i)
			for(int j = 1; j <= V; ++ j)
				dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
}

7.6 單源最短路 - Dijkstra

struct zt{
	int u, d;
};
bool operator < (zt a, zt b) {
	return a.d > b.d;
}
priority_queue <zt> q;
#define INF (1e9)
void Dijkstra(int s) {
	fill(dis+1,dis+V+1,INF);
	dis[s] = 0;
	q.push((zt){s,0});
	while(q.size()) {
		int x = q.top().u;
		q.pop();
		if(done[x]) continue;
		done[x] = 1;
		for(int i = first[x]; i != -1; i = nxt[i]) {
			int v = es[i].to;
			if(dis[v] > dis[x] + es[i].cost) {
				dis[v] = dis[x] + es[i].cost;
				q.push((zt){v,dis[v]});
			}
		}
	} 
}

7.7 差分約束系統

在這裏插入圖片描述
在這裏插入圖片描述

7.8 強聯通分量

在這裏插入圖片描述

8 dp

8.1 LIS & LCS

LIS是最長上升子序列,LCS是最長公共子序列。
求解LIS時,dp[i]dp[i]表示在結尾是a[i]a[i]的最長答案,所以dp[i]=max(dp[i],dp[j]+1)dp[i] = max(dp[i], dp[j] + 1),條件是當a[i]>a[j]a[i] > a[j]的時候更新,保證有序。
求解LCS時,用dp[i][j]dp[i][j]來表示aa序列末尾在ii之前,bb序列在jj之前的最優答案,轉移情況分爲兩種,當a[i]=b[i]a[i] = b[i]的時候,dp[i][j]=dp[i1][j1]+1dp[i][j]=dp[i-1][j-1]+1,就相當於加上了當前相等的這個元素;當a[i]b[i]a[i] \neq b[i]的時候,不能加加一,需要從之前的狀態取最大值,所以dp[i][j]=max(dp[i1][j],dp[i][j1])dp[i][j] = max(dp[i-1][j], dp[i][j-1])

    for (int i = 1; i <= n; ++i) dp1[i] = 1;
    int ans = 1;
    for (int i = 2; i <= n; ++i) {
        for (int j = 1; j < i; ++j) {
            if (A[i] > A[j]) {
                dp1[i] = max(dp1[i], dp1[j]+1);
            }
        }
        ans = max(ans, dp1[i]);
    }
    cout << ans << " "; 
    for (int i = 0; i < n; ++i) dp2[i][0] = 0;
    for (int j = 0; j < m; ++j) dp2[0][j] = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (A[i] == B[j]) {
                dp2[i][j] = dp2[i-1][j-1] + 1;
            } else {
                dp2[i][j] = max(dp2[i-1][j], dp2[i][j-1]);
            }
        }
    }
    cout << dp2[n][m] << endl;

8.2 揹包問題

    for(int i = 1; i <= M; i ++)
        for(int j = 1; j <= T; j ++)
            if(j-c[i] >= 0)
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-c[i]]+w[i]);
            else dp[i][j] = dp[i-1][j];
    cout << dp[M][T] << endl;

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

8.3 區間型dp

在這裏插入圖片描述
石子歸併:

    for(int i = 1; i <= n; i ++)
        scanf("%d", &sum[i]);

    for(int i = 1; i <= n; i ++)
        sum[i] += sum[i-1];
    memset(dp,63,sizeof(dp));
    for(int i = 1; i <= n; i ++) dp[i][i] = 0;

    for(int i = n-1; i >= 1; i --)
        for(int j = i+1; j <= n; j ++)
            for(int k = i; k <= j; k ++)
                dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);

    cout << dp[1][n] << endl;

8.4 狀態壓縮dp

在這裏插入圖片描述

8.4 單調隊列優化

在這裏插入圖片描述

8.5 樹形dp

在這裏插入圖片描述

9 矩陣

9.1 矩陣乘法

在這裏插入圖片描述

9.2 矩陣快速冪

在這裏插入圖片描述

9.3 求解線性遞推

在這裏插入圖片描述

9.4 矩陣快速冪優化dp

在這裏插入圖片描述

10 字符串

10.1 hash

在這裏插入圖片描述
std::hash:

    hash<string> my_hash;
    unsigned long long iic = (unsigned long long)my_hash("yqynb");
    cout << iic << endl;
    unsigned long long ull = std::hash<std::string>()( "yqynb" );
    cout << ull << endl;

附錄

烏龜棋

    for(int i = 0; i < n; ++ i)
        scanf("%d", &maps[i]);
    for(int i = 1; i <= m; ++ i){int x; scanf("%d", &x); ++ ka[x];}

    for(int i = 0; i <= ka[1]; ++ i)
        for(int j = 0; j <= ka[2]; ++ j)
            for(int k = 0; k <= ka[3]; ++ k)
                for(int l = 0; l <= ka[4]; ++ l)
                {
                    int tmp = 0;
                    if(i) tmp = max(tmp,dp[i - 1][j][k][l]);
                    if(j) tmp = max(tmp,dp[i][j - 1][k][l]);
                    if(k) tmp = max(tmp,dp[i][j][k - 1][l]);
                    if(l) tmp = max(tmp,dp[i][j][k][l - 1]);
                    dp[i][j][k][l] = tmp + maps[i*1+j*2+k*3+l*4];
                }
    cout << dp[ka[1]][ka[2]][ka[3]][ka[4]] << endl;

gcd

int gcd(int a, int b) {
	if(!b) return a;
	return gcd(b,a%b);
}

埃氏篩法

	for(int i = 2; i <= n; ++ i)
		if(!vis[i])
			for(int j = i+i; j <= n; j += i)
				vis[j] = 1;
	for(int i = 2; i <= n; ++ i)
		if(!vis[i])
			printf("%d ", i);
	printf("\n");

spfa

#define INF (1e9)
queue <int> q;
void spfa(int s) {
	fill(dis+1,dis+V+1,INF);
	dis[s] = 0;
	q.push(s);
	used[s] = 1;
	while(q.size()) {
		int x = q.front();
		q.pop();
		used[x] = 0;
		for(int i = first[x]; i != -1; i = nxt[i]) {
			int v = es[i].to;
			if(dis[v] > dis[x] + es[i].cost) {
				dis[v] = dis[x] + es[i].cost;
				if(!used[v]) {
					q.push(v);
					used[v] = 1;
				}
			}
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章