1. 程式人生 > 實用技巧 >2020解題記錄

2020解題記錄

11.15 樹

  • 小球-drop
    • 大事化小,n層化n-1層,計算進入該結點的次數
  • 二叉樹遍歷-flist
    • 中序+層序==前序
    • 遞迴+按層遍歷=前序遍歷
    • rank表,尋找最小結點
    • 子樹上結點號最小的結點是子樹的根結點
  • FBI樹
    • 同時進行遞迴計算FBI和後序遍歷
  • 猜數字
    cout<<(min+max)/2<<endl;
    
  • 人以群分
    • 單調序列
  • 最小環
    • gcd多個最小環
    • 物以類聚(如1、3、5、6、4、2)

11.17 樹+堆

超級對拍程式

rem Name: Batch For Check
rem Copyright: All Right Reserved
rem Author: Chen Jie rem Date: 2020/10/17 rem Description: RENAME the *.bat file and run it. rem The batch will consider succesive spaces as one. @echo off ::no parameter then begin for loop if "%1"=="" goto loop set BAT_NAME=%~nx0 ::BAT_NAME=TFILE_NAME.bat set FILE_NAME=%BAT_NAME:~0,-4% ::if no exist the corresponding input file, then end the batch
if not exist %FILE_NAME%%1.in ( goto end ) if exist %FILE_NAME%%1.out ( set ANS_NAME=%FILE_NAME%%1.out ) if exist %FILE_NAME%%1.ans ( set ANS_NAME=%FILE_NAME%%1.ans ) copy %FILE_NAME%%1.in %FILE_NAME%.in >nul echo Problem Test Data %1 time<enter %FILE_NAME% < %FILE_NAME%.in > %FILE_NAME%.out time
<enter
::if exist error, then pause for showing the difference fc %FILE_NAME%.out %ANS_NAME% /w /n if errorlevel 1 ( pause>nul ) del %FILE_NAME%.in del %FILE_NAME%.out ::end the batch goto end :loop ::call the corresponding data batch for %%i in (0 1 2 3 4 5 6 7 8 9 10 11) do call %0 %%i echo Press Any Key To Exit ... pause>nul :end

就地建堆函式

  • make_heap(),pop_heap(),push_heap()
    C++中的make_heap(), pop_heap()的標頭檔案為<algorithm>。作用與priority_queue<>;中的成員函式相同,可以單獨使用。
    
    make_heap(begin,end,greater<int>());
    
    在容器範圍內,就地建堆,保證最大值在所給範圍的最前面,其他值的位置不確定
    pop_heap(begin,end,greater<int>());
    
    將堆頂(所給範圍的最前面)元素移動到所給範圍的最後,並且將新的最大值置於所給範圍的最前面
    push_heap(begin,end,greater<int>());
    
    當已建堆的容器範圍內有新的元素插入末尾後,應當呼叫push_heap將該元素插入堆中。
code到word帶格式複製
  • 二叉樹輸出(btout)
    • 一個數組能解決的事不要用結構體
  • 查詢二叉樹(tree_a)
  • 對稱二叉樹(tree_c)
  • 合併果子(fruit)

11.18 堆+圖

最小字典序Euler迴路(逆序)

  • 騎馬修柵欄(fence)
    void find(int now)
    {
        for (int k = mi; k <= ma; k++)
            if (available[now][k] > 0)
            {
                available[now][k]--;
                available[k][now]--;
                find(k);
            }
        c[++cnt] = now;
    }
    

命令引數

  • VS2019-專案-屬性-除錯-命令引數

    • 命令列格式指定輸入檔案
      < $(ProjectDir)$(Configuration)\$(ProjectName).in
      
  • 基於紅黑樹的可重複集合

    multiset<int>st;
    st.insert(a);
    cout << *st.begin() << ' ';
    st.erase(st.begin());
    cout << *(--st.end()) << endl;
    st.erase(--st.end());
    
  • 看病-hp

    • release比debug快,約4~6~9
    • 簡單的程式倍數高
      0.11-0.96
      0.7-4
      1.4-5.77
      
  • 小明的賬單-bill

    • heap不會錯
    • multiset太慢
  • 鏟雪車(snow)

    • 加起來就好了,別多想
  • 珍珠(bead)

    • 不允許指向引用的指標、迭代器,即不允許定義
      vector<typename&>vec;
      

11.19 第三節 最短路徑演算法

FloyedΘ(N3)Floyed \Theta(N^3)FloyedΘ(N3)

  • 適用於出現負邊權的情況
    初始化:點u、v如果有邊相連,則dis[u][v]=w[u][v]。
    如果不相連則dis[u][v]=INT_MAX
    
    for (k = 1; k <= n; k++)
        for (i = 1; i <= n; i++)
            for (j = 1; j <= n; j++)
                if (dis[i][j] > dis[i][k] + dis[k][j])
                    dis[i][j] = dis[i][k] + dis[k][j];
    
  • 牛的旅行-travel
    這兩個牧場都在John的農場上。John將會在兩個牧場中各選一個牧區,然後用一條路徑連起來,使得連通後這個新的更大的牧場有最小的直徑。注意,如果兩條路徑中途相交,我們不認為它們是連通的。只有兩條路徑在同一個牧區相交,我們才認為它們是連通的。
    現在請你程式設計找出一條連線兩個不同牧場的路徑,使得連上這條路徑後,這個更大的新牧場有最小的直徑。
    
    用Floyed求出任兩點間的最短路,然後求出每個點到所有可達的點的最大距離,記做mdis[i]。(Floyed演算法) 
    r1=max(mdis[i]);
    然後列舉不連通的兩點i,j,把他們連通,則新的直徑是mdis[i]+mdis[j]+(i,j)間的距離。 
    r2=min(mdis[i]+mdis[j]+dis[i,j]);
    re=max(r1,r2);//舊最大與新直徑的最小的最大
    re就是所求。
    

DijkstraΘ(N2)Dijkstra \Theta(N^2)DijkstraΘ(N2)

  • 不能處理存在負邊權的情況
    設起點為s,dis[v]表示從s到v的最短路徑,pre[v]為v的前驅節點,用來輸出路徑。
    a)初始化:dis[v]=(v≠s); dis[s]=0; pre[s]=0; 
    b)For (i = 1; i <= n ; i++)
        1.在沒有被訪問過的點中找一個頂點u使得dis[u]是最小的。
        2.u標記為已確定最短路徑
        3.For與u相連的每個未確定最短路徑的頂點v
            if (dis[u] + w[u][v] < dis[v]) 
            {
                dis[v] = dis[u] + w[u][v];
                pre[v] = u;
            }
    c)演算法結束:dis[v]為s到v的最短距離;pre[v]為v的前驅節點,用來輸出路徑。
    
    for (int i = 1; i <= n; ++i)
    {
        int k = 0;
        for (int j = 1; j <= n; ++j)
            if (!ok[j] && ans[j] < ans[k])
                k = j;
        if (!k)break;
        ok[k] = true;
        for (int j = 1; j <= n; ++j)
            if (!ok[j] && ratio[j][k])
                ans[j] = std::min(ans[j], ans[k] * ratio[j][k]);
    }
    

Bellman−FordΘ(NE)Bellman-Ford \Theta(NE)BellmanFordΘ(NE)

  • 能夠處理存在負邊權的情況,但無法處理存在負權迴路的情況
    設s為起點,dis[v]即為s到v的最短距離,pre[v]為v前驅。w[j]是邊j的長度,且j連線u、v。
    初始化:dis[s]=0, dis[v]=∞(v≠s),pre[s]=0
    For (i = 1; i <= n-1; i++)
        For (j = 1; j <= E; j++)//注意要列舉所有邊,不能列舉點。
            if (dis[u] + w[j] < dis[v])//u、v分別是這條邊連線的兩個點。
            {
                dis[v] = dis[u] + w[j];
                pre[v] = u;
            }
    
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            dis[f[j].y] = std::min(dis[f[j].y], dis[f[j].x] + f[j].w);
            dis[f[j].x] = std::min(dis[f[j].x], dis[f[j].y] + f[j].w);
        }
    

SPFAΘ(kE)SPFA \Theta(kE)SPFAΘ(kE)

  • Bellman−FordBellman-FordBellmanFord演算法的佇列實現
  • kkk的平均值是2
    dis[i]記錄從起點s到i的最短路徑,w[i][j]記錄連線i,j的邊的長度。pre[v]記錄前趨。
    team[1..n]為佇列,頭指標head,尾指標tail。
    布林陣列exist[1..n]記錄一個點是否現在存在在佇列中。
    初始化:dis[s]=0,dis[v]=∞(v≠s),memset(exist,false,sizeof(exist));
    起點入隊team[1]=s; head=0; tail=1;exist[s]=true;
    do
    {
    1、頭指標向下移一位,取出指向的點u。
    2、exist[u]=false;已被取出了佇列
    3、For與u相連的所有點v//注意不要去列舉所有點,用陣列模擬鄰接表儲存
        if (dis[v]>dis[u]+w[u][v])
        {
            dis[v]=dis[u]+w[u][v];
            pre[v]=u;
            if (!exist[v])//佇列中不存在v點,v入隊。
            {
                //尾指標下移一位,v入隊;
                exist[v]=true;
            }
        }
    }
    while (head < tail);
    
    dis[i] = 0;
    team[0] = i;
    int head = 0, tail = 1;
    exist[i] = true;
    do
    {
        int u = team[head];
        if (++head == 2 * 牧場)head = 0;
        exist[u] = false;
        for (int j = 1; j <= num[u]; ++j)
        {
            int v = a[u][j];
            if (dis[v] > dis[u] + w[u][v])
            {
                dis[v] = dis[u] + w[u][v];
                if (!exist[v])
                {
                    team[tail] = v;
                    if (++tail == 2 * 牧場)tail = 0;
                    exist[v] = true;
                }
            }
        }
    } while (head != tail);
    

11.20

int a[5] = { 1,2,3,4,5 };
int* p = a;
&(p + 2);//“&”要求左值
&*(p + 2);//等於a + 2

11.21 第四節 圖的連通性問題

一、判斷圖中的兩點是否連通

  • Floyed演算法
    • 時間複雜度:Θ(N3)\Theta(N^3)Θ(N3)
  • 遍歷演算法
    • 時間複雜度:Θ(N2)\Theta(N^2)Θ(N2)

二、最小環問題

最小環就是指在一張圖中找出一個環,使得這個環上的各條邊的權值之和最小。在Floyed的同時,可以順便算出最小環。
記兩點間的最短路為dis[i][j],g[i][j]為邊<i,j>的權值。
for (k = 1; k <= n; k++)
{
    for (i = 1; i <= k - 1; i++)
        for (j = i + 1; j <= k - 1; j++)
            answer = min(answer, dis[i][j] + g[j][k] + g[k][i]);
    for (i = 1; i <= n; i++)
        for (j = 1; j <= n; j++)
            dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
answer即為這張圖的最小環。
一個環中的最大結點為k(編號最大),與它相連的兩個點為i,j,這個環的最短長度為g[i][k]+g[k][j]+(i到j的路徑中,所有結點編號都小於k的最短路徑長度)。
根據Floyed的原理,在最外層迴圈做了k-1次之後,dis[i][j]則代表了i到j的路徑中,所有結點編號都小於k的最短路徑。
綜上所述,該演算法一定能找到圖中最小環。

三、求有向圖的強連通分量

Kosaraju演算法可以求出有向圖中的強連通分量個數,並且對分屬於不同強連通分量的點進行標記。它的演算法描述較為簡單:
(1) 第一次對圖G進行DFS遍歷,並在遍歷過程中,記錄每一個點的退出順序。
(2)倒轉每一條邊的方向,構造出一個反圖G’。然後按照退出順序的逆序對反圖進行第二次DFS遍歷。
每次遍歷得到的那些點即屬於同一個強連通分量。

憑本事打的表!!

  • 組合數

11.22

  • B. Deadline Management
    • 輸入不保證有序
      • pair<int, int>t[N];
  • C. 撕紙條
    • 與順序無關
      • 不用儲存陣列
  • F. 加減乘除
    • 函式指標陣列
      • 4個if也可
      • 用一張牌去匹配另一張牌
    bool (*check[4])(int, int) = { check1,check2,check3,check4 };
    

QQ截圖複製色號

  • 按C複製色號dd,dd,dd,Ctrl+C複製16進位制色號#hhhhhh