CSP-S第二輪認證總結——提高組算法總結

 

目錄

0.前言

一.動態規劃——必考必考必考!!!

1.揹包

(1)01揹包

(2)完全揹包

2.線性DP

3.多維DP

二.貪心

三.模擬

四.圖論——很靈活

1.最短路

(1)spfa

(2)Dijkstra(+堆優化)時間複雜度遠快於spfa

2.最小生成樹

3.強連通分量

(1)有向圖(含割點+縮點)

(2)無向圖(含割點)

(3)無向圖求每個連通分量

4.二分圖匹配

四.數論

1.逆元

2.組合數

3.歐拉函數

(1)歐拉定理

(2)指數循環節

4.擴展歐幾里得——求解二元一次方程組

5.中國剩餘定理(CRT)

五.搜索

1.記憶化

2.剪枝——最重要的技巧

3.看範圍答題

謝謝!

安心爆零,平安退役!


0.前言

算法這種東西,有時候其實並不管用,還是一波暴力走人,退役。。。。。。乾脆。

一.動態規劃——必考必考必考!!!

DP這種算法,不是說了概念就能懂得,只有身經百戰,然後靠一波運氣纔行。下面有一些常見的DP考法:

1.揹包

這種DP方式很常見,並且範圍很廣,而且有較明顯的方法提示。就是在題目有求和的最大的限制,並且給你一個數列的時候,肯定是揹包,說都不用說。還有個更明顯的標誌:每個數及其和不大!這裏有兩種常考揹包:

(1)01揹包

for (int i = 1; i <= n; i ++){//n表示物品種數
	for (int j = m; j >= w[i]; j --){//m表示揹包容量
		dp[j] = max (dp[j], dp[j - w[i]] + v[i]);//w表示每種物品的體積大小,v則表示價值
	}
}
for (int i = 1; i <= m; i ++)
	ans = max (dp[i], ans);

(2)完全揹包

for (int i = 1; i <= n; i ++){//各變量含義同上
	for (int j = w[i]; j <= m; j ++){//與01揹包反過來
		dp[j] = max (dp[j], dp[j - w[i]] + v[i]);
	}
}
for (int i = 1; i <= m; i ++)
	ans = max (dp[i], ans);

但是不能完全靠這板子去得分,題目經常都很靈活,但是隻要稍微改動一下就行了。比如:讓揹包去存已經用的物品個數,因爲題目限制了物品個數。

典例:NOIP2018 提高組Day1 T2 貨幣系統

2.線性DP

這種DP就包括最長公共上升子序列,最長上升(下降)子序列,單調隊列,背一下,這種題目挺好做的。

3.多維DP

這是最難想的DP類型的題目,通常在一個矩陣的背景下進行,這種特別難,只有多花時間想一想,有幾個經典的類型:

1.最大子矩陣:一行一行的往下轉移

2.創意喫魚法:一層一層的往左和往下擴展

總而言之,從一個下標轉移到下一個下標,或從一排轉移到下一排的轉移方式,是DP的精髓。

DP優化我就不想說了,比如斜率優化、平行四邊形優化根本就不是我的料。

二.貪心

貪心和動態規劃容易搞混,但是記住,需要狀態轉移,有多種情況的就是動態規劃,所謂多種情況,就是你貪心考慮不到。

貪心的精髓就是。所以貪心經常需要排序,這有點玄學,反正我有點懵,憑着感覺去貪就行了。

三.模擬

我認爲,這個就不叫算法,其實就考察一個東西:細心

四.圖論——很靈活

這個算法不僅內容多,而且考的非常靈活,經常讓你手足無措,最後看了題解才發現就一個最小生成樹(舉個例子)而已。

這裏先上模板:

1.最短路

spfaDijkstra(floyd就不說了)

(1)spfa

這個我就不想說了

(2)Dijkstra(+堆優化)時間複雜度遠快於spfa

點擊打開鏈接

注意,Dijkstra是不能有負環的,是有spfa才能判環(若一個點進入n+1次,就存在環)

2.最小生成樹

直接上了:

bool cmp (int x, int y){
    return w[x] < w[y];
}
void makeSet (int x){
    for (int i = 1; i <= x; i ++)
        fa[i] = i;
}
int findSet (int x){
    if (x != fa[x])
        fa[x] = findSet (fa[x]);
    return fa[x];
}
void unionSet (){
    for (int i = 1; i <= k; i ++){
        int e = r[i], U = findSet (u[e]), V = findSet (v[e]);//特別注意是u[e]而不是u[i],我曾經錯過
        if (U != V){
            ans += w[e];
            fa[U] = V;
            Sum ++;
        }
    }
}
int main (){
    scanf ("%d %d", &n, &m);
    makeSet (n);
    for (int i = 1; i <= m; i ++){
        k ++;
        scanf ("%d %d %d", &u[k], &v[k], &w[k]);
        k ++;
        u[k] = v[k - 1];
        v[k] = u[k - 1];
        w[k] = w[k - 1];
    }
    for (int i = 1; i <= k; i ++)
        r[i] = i;
    sort (r + 1, r + 1 + k, cmp);
    unionSet ();
}

3.強連通分量

這個算法你不能打錯任何一個字母,因爲一點細微的差錯可能導致全盤崩潰。但是這個算法考的可能性不大

(1)有向圖(含割點+縮點)

void Tarjan (int u){//求強連通分量
    DFN[u] = LOW[u] = ++ now;
    instack[u] = 1;
    s.push (u);
    for (int i = 0; i < G[u].size (); i ++){
        int v = G[u][i];
        if (! DFN[v]){
            Tarjan (v);
            LOW[u] = min (LOW[v], LOW[u]);
        }
        else if (instack[v]){
            LOW[u] = min (DFN[v], LOW[u]);
        }
    }
    if (DFN[u] == LOW[u]){
        num ++;
        int v;
        do {
            v = s.top ();
            s.pop ();
            instack[v] = 0;
        }while (u != v);
    }
}

(2)無向圖(含割點)

void Tarjan (int x, int fa){
    dfn[x] = low[x] = ++ cntd;
    int children = 0;
    for (int i = 0; i < G[x].size (); i ++){
        int tmp = G[x][i];
        if (! dfn[tmp]){
            children ++;
            Tarjan (tmp, x);
            if (low[tmp] >= dfn[x])
                isgedian[x] = 1;
            low[x] = min (low[x], low[tmp]);
        }
        else if (dfn[x] > dfn[tmp] && fa != tmp){//注意fa!=tmp
            low[x] = min (low[x], dfn[tmp]);
        }
    }
    if (fa < 0 && children == 1)//注意
        isgedian[x] = 0;
}

(3)無向圖求每個連通分量

void DFS (int x){
    vis[x] = num;
    sum ++;
    for (int i = 0; i < G[x].size (); i ++){
        int tmp = G[x][i];
        if (isgedian[tmp] && vis[tmp] != num){//注意vis[tmp]!=num
            cut ++;
            vis[tmp] = num;
        }
        else if (! vis[tmp])
                DFS (tmp);
    }
}

4.二分圖匹配

本蒟蒻實在太弱,不會模板,但一定要了解它的意思,概念。

四.數論

這個算法對於我來說就是學了也不會,因爲只會板子,做題怎麼也做不起,我太難了。但是有幾個必背的板:

1.逆元

a/bmodc = a*b^{c - 2}modc

rfac[0] = rfac[1] = 1;//rfac表示逆元,如果空間不滿足,用qkpow暴力求
for (int i = 2; i <= MAXN; i ++)
    rfac[i] = rfac[mod % i] * (mod - mod / i) % mod;

2.組合數

C_{m}^{n}=\frac{m!}{n!(m - n)!}

//法一:特別快
int C (int m, int n){
    return fac[m] * rfac[n] % mod * rfac[m - n] % mod;
}
//法二:基礎
int C (int m, int n){
    if (n > m / 2)
        n = m - n;
    int ans = 1;
    for (int i = 1; i <= n; i ++){
        ans = ans * (m - i + 1) / (n - i + 1);
    }
    return ans;
}

優化:Lucas定理

C_{n}^{m}modp=C_{n mod p}^{m mod p}*Lucas_{n/p}^{m/p}modp

int Lucas (int n, int m){
    if (n == m || !m)
        return 1;
    return C (m % p, n % p) * Lucas (m / p, n / p) % p;
}

常用處:楊輝三角

遞推式:f[i][j]=f[i-1][j] + f[i-1][j-1]

3.歐拉函數

int phi (int x){
    int res = x;
    for (int i = 1; i * i <= x; i ++){
        if (x % i == 0){
            res = res / i * (i - 1);
            while (x % i == 0)
                x /= i;
        }
    }
    if (x > 1)
        res = res / x * (x - 1);
    return res;
}

相關定理:

(1)歐拉定理

a^{\phi (p)}\equiv 1(modp)(a與p互質)

(2)指數循環節

常用於指數太大時。

a^{b}modp=a^{bmod\phi(p)+\phi(p)}modp(可一直遞推下去)

4.擴展歐幾里得——求解二元一次方程組

ax+by=1(gcd(a,b)=1)

int exgcd (int a, int b, int &x, int &y){
    if (!b){
        x = 1, y = 0;
        return a;
    }
    int r = exgcd (b, a % b, y, x);
    y -= a / b * x;//核心關鍵
    return r;
}

5.中國剩餘定理(CRT)

//b[i]是模數,p[i]是同於是右邊的ai,M是所有模數的公倍數
void exgcd (int a, int b, int &x, int &y){
    if (! b){
        x = 1;
        y = 0;
        return ;
    }
    exgcd (b, a % b, y, x);
    y -= a / b * x;
}
int main (){
    int ans = 0;
    int x, y;
    for (int i = 1; i <= n; i ++){
        int tp = M / b[i];
        exgcd (tp, b[i], x, y);
        ans = (ans + p[i] * tp * x) % M;
    }

五.搜索

這種算法,相信都家喻戶曉。但是,同樣是爆搜,人與人做出來卻不一樣。有的人只能卡個暴力分,而有的人他就A了,連DP題目他都能用搜索過掉,這就是搜索技巧的問題了(隆重爲大家推薦一個大佬,曾經用搜索過掉了分組揹包,驚呆了(點擊:孫子))。下面隆重介紹兩個在考試時能夠發揮神威的技巧:

1.記憶化

只要空間允許,這種方法就能省掉重複計算的過程,快得一批。

2.剪枝——最重要的技巧

剪枝十分重要,但是十分難想,甚至有時後很玄學,但是這裏有幾個常見的技巧:

(1)如果當前的答案已經不比已經得出的答案優,就直接回溯;

(2)如果當前的值不行,就不要去試後面重複的值,通常附帶一個排序。

3.看範圍答題

這是最有趣的,大家要有想象力,說不定搜一半,再DP一般就過了呢?或者。。。。。。

典例:

小奇取石子

但是重點在於:看數據範圍,數據大的稍暴力,反之DP或優化。額,有點騙分的樣子。。。。。。

謝謝!

安心爆零,平安退役!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章