[Luogu ブラシの質問] Blue Bridge Cup トピックのブレークスルー - 深さ優先検索 - dfs (3)

前に書かれています:

どうすればアルゴリズムをうまく学習できますか?

個人的には質問の体系的なブラッシングが特に重要だと思いますが、

そのため、深さ優先探索を上手に学ぶために、暴力的な探索を使ってブルーブリッジカップに対処するために、

さっそくクイズを始めましょう!

タイトル: P1088 [NOIP2004 普及グループ] The Martian - Luogu | コンピュータ サイエンス教育の新しいエコロジー (luogu.com.cn)

タイトル説明:

入力形式:

3行あります。
最初の行には、火星の指の数を表す正の整数 N が含まれています (1 ≤ N ≤ 10000)。
2 行目は正の整数 M で、加算する短整数 (1 ≤ M ≤ 100) を表します。
次の行は、スペースで区切られた 1 から N までの N 個の整数の配列であり、火星の指の配列順序を示しています。

出力フォーマット:

変更された火星の指の配置順序を表す N 個の整数。

隣接する 2 つの数字はスペースで区切られ、余分なスペースは使用できません。

入力サンプル:

5
3
1 2 3 4 5

出力例:

1 2 4 5 3

ヒント:

データの 30% では、N≤15。

データの 60% では、N≤50。

データの 100% の場合、N≤10000。

問題解決のアイデア:

深さ優先探索を使うと、

最初の注意点は、検索の順序です。

確認したいので、

私たちが書いた再帰構造は、すべてのケースをトラバースできます。

検索を初めて学ぶとき、再帰的な検索ツリーの観測を描画する必要があります。

再帰は非常に抽象的で、絵を描くことで問題を解決することができます。(上記の再帰的検索の基本的な考え方に慣れておくことは常に良いことです)

次は具体的なアイデア です

トピックの要件に従って、最初に再帰的な検索ツリーを描画します。

ルート ノード: (たとえば、エイリアンには 5 本の指があります)

各場所に配置できるデータに基づいて検索します。

 一つ一つ挙げるには多すぎて、

最初に検索されたパスを描画し、

一般的なアイデアを見つけてください:

下に検索を続けます。

 最後に、次のことがわかりました。

 次に、タイトルはカウントの開始位置を示します。

逆に3つ数えて、数えた後の指の配置を出力してみましょう。

ルールを要約した後:

指の配置は大まかに上図のようになっています。

12345の初期配置から、12453までの3段を検索して出力します。 

コードの実装は次のとおりです。

コード:

//包好头文件
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

//外星人有手指 < 一万
const int N = 10010;

int n, m;
int cnt;
int flag;

//存数据
int arr[N];

//存外星人手指的初始排列
int cmp[N];

//判断数字有没有使用过
bool used[N];

void dfs(int u)
{
    //如果已经找到,直接返回(剪枝)
    if(flag)
    return;
    
    if(u > n)
    {
        //证明已经搜索了一次
        cnt++;
        if(cnt == m + 1)
        {
            flag++;
            for(int i = 1; i <= n; i++)
            {
                printf("%d ", arr[i]);
            }
        }
        return;
    }
    
    for(int i = 1; i <= n; i++)
    {
        //如果是第一次搜索,直接从外星人手指的初始排列开始
        if(!cnt)
        {
            i = cmp[u];
        }
        if(!used[i])
        {
            arr[u] = i;
            used[i] = true;
            dfs(u + 1);
            used[i] = false;//该位置改为没用过
        }
    }
}

int main()
{
    scanf("%d %d", &n, &m);
    
    //存手指的初始排列
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &cmp[i]);
    }
    
    dfs(1);
    
    return 0;
}

交流 !!!!!!!!!!

最後に書く:

以上がこの記事の内容です、お読みいただきありがとうございます。

この記事が気に入ったら、いいねとコメントをお願いします。また、ご意見をお書きください。

私と一緒にプログラミングを学びたい場合は、私に従ってください。私たちは一緒に学び、成長します。

今後もより質の高いコンテンツを出力していきますので、よろしくお願いします。

おすすめ

転載: blog.csdn.net/Locky136/article/details/129524428