1. 程式人生 > 其它 >AcWing 藍橋杯專題訓練 :(一)遞迴與遞推

AcWing 藍橋杯專題訓練 :(一)遞迴與遞推

技術標籤:藍橋杯集訓c++遞迴演算法dfs演算法

AcWing 藍橋杯專題訓練 :(一)遞迴與遞推

AcWing賬號ID:田所浩二

注:可能會和y總的程式碼有不一樣的地方

  1. 遞迴實現指數型列舉(掌握)
    從 1~n 這 n 個整數中隨機選取任意多個,而且題目最後要求我們以升序的形式輸出。
    在y總課堂上有明確的指出我們可以用樹狀圖來考慮,對於每一位數,我們都能衍生出選和不選兩種情況,考慮玩兩種情況後同理我們再考慮他們的下一位數的選和不選…依次類推。只要把這兩種情況分開考慮即可,注意遞迴還原
#include <cstdio>
#include <cstring>
#include
<cstdlib>
#include <iostream> #include <algorithm> using namespace std; const int N=16; int n; vector<int>path;//記錄最終答案 void dfs(int u)//u代表了當前是1-n中的哪一個數,由於輸出需要從小到大,故從1開始 { if(u == n+1)//如果這n位數都列舉完了,顯然是列舉到第n+1個了 { for(int i=0;i<path.size();i++) { cout<<
path[i]<<" "; } cout<<endl; return ;//遞迴終止,開始回溯 } path.push_back(u); dfs(u+1);//如果選了本位數,就push進去 path.pop_back();//遞迴還原 dfs(u+1);//如果沒選本位數。 } int main() { cin>>n; dfs(1); return 0; }
  1. 遞迴實現指數型列舉(掌握)
    這道題要求我們打亂1-n的順序,同時要求字典序較小的在前。所以我們用for去列舉我們1 - n中的每一位數並且標記我們已經使用過這位數,在這裡我們用遞迴的思路去解決:
    以i = 1 位例,我們將第一個數選擇為 i = 1並且將其 push進path陣列後,那麼我們遞迴到下一層就是從 i=2->n 裡選一個數。
    同理我們第一層遞迴也可以不選 i= 1 ,通過for 我們可以選擇 i=2,3…n 作為第一個數並標記防止重複後,然後遞迴到下一層從沒有標記過的數中選則自己的下一位數。同樣的注意遞迴還原
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=10;
int n;
vector<int>path;
bool st[N];

void dfs(int u)
{
    if(u == n)
    {
        for(int i=0;i<path.size();i++)
        {
            cout<<path[i]<<" ";
        }
        cout<<endl;
        return ;
    }
    for(int i=1;i<=n;i++)
    {
        if(!st[i])
        {
            path.push_back(i);
            st[i]=true;
            dfs(u+1);
            st[i]=false;
            path.pop_back();
        }
    }
}
int main()
{
    cin>>n;
    dfs(0);
    return 0;
}
  1. 斐波那契數列(掌握)
    這道題就沒有什麼好說的了,就是普通的遞推。
#include<bits/stdc++.h>
using namespace std;
int a[100010];
int main()
{
    int n;
    cin>>n;
    if(n==1)
    {
        cout<<"0"<<endl;
    }
    else
    {
    a[0]=0;
    a[1]=1;
    cout<<"0"<<" "<<"1"<<" ";
    for(int i=2;i<=n-1;i++)
    {
        a[i]=a[i-1]+a[i-2];
        cout<<a[i]<<" ";
    }
    cout<<endl;
    }
    return 0;
}
  1. 費解的開關(儘量瞭解)
    這道題的核心思路在於:
    首先每個格子實際上只需要按一下,如果按偶數下那麼沒有效果,如果按奇數就相當於偶數下+1,和按一下相同,不符合最小步數原理。
    同時燈泡的亮/不亮相當於二進位制的0/1,我們可以通過位運算控制燈的關閉和開啟。
//首先每個格子都只能按一下,如果按偶數下那麼沒有效果,如果按技術下,又和1下相同,不符合最小步數原理
#include<bits/stdc++.h>
using namespace std;
const int N=6;
char g[N][N],backup[N][N];
int dx[5]={-1,0,1,0,0};
int dy[5]={0,1,0,-1,0};
void turn(int x,int y)
{
    for(int i=0;i<5;i++)
    {
        int a=x+dx[i];
        int b=y+dy[i];//二進位制48 49 分別為110000,110001,只差1,所以可以亦或
    if(a<0||a>=5||b<0||b>=5)
    {continue;}
    g[a][b]^=1;
    }//由於字元型0的asicii值為48,1的為49,亦或即便為對位上的相反的數,所以只有最後一位對位相反
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        for(int i=0;i<5;i++)
        {
            cin>>g[i];
        }
        int res=10;//最大步數
        for(int op=0;op<32;op++)//第一行可以變成32種可能性,依據按與不按分為32種情況,設1為可以0為不可以
        {//例如10001為操作第一位和最後一位
            memcpy(backup,g,sizeof g);
            int step=0;
            for(int i=0;i<5;i++)
            {
                if(op>>i&1)//二進位制位運算,代表著如果op的二進位制數中為0則不操作,為1則可操作(選或不選問題)
                {//所以對5位二進位制數的每一位列舉只要有一就改變
                    step++;
                    turn(0,i);//改變原有的亮光程度
                }
            }
            for(int i=0;i<4;i++)//一次列舉剩下的4x5
            {
                for(int j=0;j<5;j++)
                {
                    if(g[i][j]=='0')
                    {
                        step++;
                        turn(i+1,j);//下面決定上面,當上面有0的時候,摁下面
                    }
                }
            }
            bool dark=false;
            for(int i=0;i<5;i++)
            {
                if(g[4][i]=='0')
                {
                    dark=true;
                    break;
                }
            }
            if(!dark)
            {
                res=min(res,step);
            }
            memcpy(g,backup,sizeof g);//還原
        }
        if(res>6)
        {
            cout<<"-1"<<endl;
        }
        else
        {
            cout<<res<<endl;
        }
    }
    return 0;
}