ブルーブリッジカップ選択された質問アルゴリズムシリーズ-コイントス-欲張り法

この列は含まれています。
今日は欲張りアルゴリズムについて学びます。

貪欲は、理解するのが最も簡単なアルゴリズムのアイデアであると言えます。問題全体を複数のステップに分解し、各ステップで、すべてのステップが終了するまで現在のステップの最適なソリューションを選択します。各ステップで、適切な効果を考慮しないでください。後続のステップの前の選択に戻って変更することもなくなりました。簡単に言えば、「一歩ずつ」「近視眼」という考え方です。

では、この貪欲な方法の使用は何ですか?
貪欲な方法には幅広い用途があります。たとえば、グラフ理論の最小スパニングツリーアルゴリズムと単一ソース最短経路アルゴリズムのダイクストラは、欲張り思考の典型的なアプリケーションです。まず、コイン問題の例を使用して、欲張り法の適用規則を導きましょう。

トピックの説明:1元、2元、5元の3種類の硬貨を持って買い物に行く人で、硬貨の数に制限はありません。M元を支払う必要があり、その数を最小限に抑えるために支払う方法を尋ねます。コイン?

生活の常識によれば、最初のステップは額面が最大の5元のコインを取り出すこと、2番目のステップは2元のコインの2番目に大きい額面を取り出すこと、そして最後に最小の1を取り出すことです。元コイン。このソリューションでは、コインの総数は最小限です。
最初にブルートフォースを使用してこのプログラムを作成しましょう。

m  = int(input())
if m>=5:
    cnt = m//5
    m = m-cnt*5
    if m == 0:
        print(cnt)
if m>=2:
    cnttwo = m//2
    cnt += m//2
    m = m-cnttwo*2
    if m == 0:
        print(cnt)
if m>=1:
    cntone = m//1
    cnt += m//1
    m = m-cntone*1
    if m == 0:
        print(cnt)

コードは単純で、計算は最小限です!欲張り法は計算の複雑さが最も低いアルゴリズムであると言えますが、ブルートフォース法は計算の複雑さが最も低くなります。
上記の例では、各ステップでコインを選択する操作は全体的な最適性からは考慮されず、現在のステップではローカルの最適性のみが選択されますが、結果はグローバルな最適性になります。

局所最適はグローバル最適につながりません。
これに注意を払わなければなりません。

たとえば、最小のコイン問題の場合、欲張り法を使用して最適な解を得ることができますか?答えはいいえだ。
パラメータを少し変更すると、必ずしも最適な解が得られるとは限らず、解があったとしても答えを計算することはできません。
例を見てみましょう:

1.最適解が得られない状況。たとえば、硬貨の金種は1、2、4、5、6元です。今、9元を支払うために、欲張り法が使用される場合、答えは6 + 2 + 1であり、これは3コインを必要とし、最適な5+4は2コインのみを必要とします。
2.答えが計算できない状況。たとえば、額面が1元のコインがある場合、貪欲によって解決策が得られることが保証されます。1元のコインがない場合、解決策がないことがよくあります。たとえば、額面が2、3、5元のコインのみが9元を支払い、欲張り法では解を得ることができませんが、解は9 = 5 + 2+2です。

したがって、最小のコインの問題では、欲張り法を使用できるかどうかは、コインの額面に関係します。貪欲は、1、2、5などの宗派、および1、2などの宗派に有効です。 、4、5、6または2、3、5そのような宗派、貪欲は無効です。
あらゆる宗派のコイン問題に対する明確な解決策はありますか?動的計画法を使用する必要があります。このアルゴリズムは、後でこの列で更新​​します。ただし、貪欲と動的計画法の関係について話す必要があります。問題が貪欲で解決できない場合は、動的計画法で解決できることがよくあります。
さて、欲張りトピックに戻ると、欲張り法は必ずしも最適な解決策を得るとは限りませんが、それは単純なアイデアを持ち、プログラミングが簡単です。したがって、欲張り法によって問題が最適に解決されると判断された場合は、それを使用する必要があります。
では、トピックを判断する方法は貪欲を使用できますか?

欲張り法によって解決される問題は、次の特性を満たす必要があります
。1.最適な部分構造特性。問題の最適解がその部分問題の最適解を含む場合、その問題は最適部分構造の性質を持っていると言われ、問題は最適性の原理を満たしているとも言われます。つまり、局所最適から大域最適に拡張することができます。
2.貪欲な選択プロパティ。問題のグローバルな最適解は、一連のローカルな最適選択によって取得できます。

欲張りアルゴリズムには固定アルゴリズムフレームワークがありません。重要なのは、欲張り戦略を選択する方法です。
いわゆる欲張り戦略には後遺症があってはなりません。つまり、特定の状態の後のプロセスは前の状態に影響を与えず、現在の状態にのみ関連します。また、巡回セールスマン問題などの困難な問題で欲張り法を使用すると、最適な解を得ることが困難になります。しかし、欲張り法は多くの場合、適切な近似解を得ることができます。最適なソリューションが必ずしも必要ではない場合、貪欲な結果も非常に優れたソリューションです。
ローカル最適の組み合わせは必ずしもグローバル最適ではないため、欲張りアルゴリズムは非常に信頼性が低いように聞こえますか?しかし、局所最適を大域最適に到達させるいくつかの規則はありますか?つまり、問題が貪欲である可能性があることを証明できる一般的な数学的理論はありますか?
いくつかの。「マロイド」と呼ばれる理論がありますが、欲張り法が適用できるすべての場合を網羅しているわけではありませんが、多くの場合、欲張り法が最適な解を生み出すことができると判断できます。あなたが興味を持っているなら、あなたは見つけることができます。
次に、いくつかのよくある質問を使用して欲張り法の考え方を構築し、次に競争の質問を使用してそれを統合します。

アクティビティスケジューリング問題(インターバルスケジューリング問題とも呼ばれます)

トピックの説明:多くのテレビ番組があり、開始時刻と終了時刻を示します。一部のプログラムには時間の競合があります。全部で何本のテレビ番組を見ることができますか?

入力:入力データには複数のテストインスタンスが含まれ、各テストインスタンスの最初の行には整数n(n <= 100)のみが含まれます。これは、プログラムの総数を表し、その後にn行のデータが続き、各行には2つのデータが含まれます。 、e(1≤i≤n)は、それぞれi番目のプログラムの開始時刻と終了時刻を表します。問題を単純化するために、各時刻は正の整数で表されます。n = 0は、入力が終了し、処理が行われないことを意味します。
出力:テストインスタンスごとに、完全に視聴できるテレビ番組の数を出力します。各テストインスタンスの出力は1行を占めます。

問題を解決するための鍵は、できるだけ多くのアクティビティを手配するための貪欲な戦略を選択することです。あなたはどのような貪欲な戦略を考えることができますか?
アクティビティには開始時間と終了時間があります...私は3つの貪欲な戦略を考えました:
1。最も早い開始時間。
2.最も早い終了時間。
3.最小限の時間を使用します。

それでは、これら3つの戦略を分析しましょう。
まず、最初の戦略は間違っています。アクティビティが遅れると、次のアクティビティを開始できないためです。
2番目の戦略は合理的であり、アクティビティをできるだけ早く終了することで、より多くの後続のアクティビティに対応できます。
そして、3番目の戦略は明らかに間違っています。

それから私達は最も早い終わりの時間のために貪欲です。アルゴリズムの手順は次のとおり
です。1。n個のアクティビティを終了時刻で並べ替えます。
2.終了する最初のイベントを選択し、その時間と競合するイベントを削除(またはスキップ)します。
3.アクティビティが空になるまで、手順2を繰り返します。残りのアクティビティの中で最も早く終了するアクティビティが選択されるたびに、その時間と競合するアクティビティが削除されます。

下の図は例です。最適なアクティビティは1、3、5であり、アクティビティ2と4は他のプログラムと競合します。画像の説明を追加してください
図1アクティビティの配置
この戦略の特性を分析してみましょう
。1。最適な部分構造特性に準拠しています。選択された最初のアクティビティは最適なソリューションである必要があります。同様に、選択された2番目のアクティビティ、3番目のアクティビティ...もこの最適なソリューションに含まれています。
2.欲張り選択プロパティに準拠しています。アルゴリズムの各ステップは、同じ欲張り戦略を使用します。
コードは次のとおりです。

s = [[] for i in range(100)]
while 1:
    n = int(input())
    if n  == 0 :
        break
    for i in range(0,n):
       a,b = map(int,input().split())
       s[i].append(a)
       s[i].append(b)
    while [] in s:
        s.remove([])
    s = sorted(s,key = lambda s:s[1])
    last = -1
    count = 0
    for i in range(0,n):
            if s[i][0] >= last:
                count += 1
                last = s[i][1]
    print(count)

見てみましょう

インターバルカバレッジの問題

トピックの説明:長さnの間隔が与えられた場合、m本の線分の左端(始点)と右端(終点)を指定します。少なくとも間隔全体を完全にカバーするために使用できる線分の数を尋ねます。

貪欲な戦略:より長い線分を見つけてみてください。
問題を解決する手順は次のとおり
です。1。左の端点に従って各線分を昇順で並べ替えます。
2.カバーされた間隔を[L、R]とし、残りの線分で、左端がR以下で、右端が最大のすべての線分を見つけ、この線分をカバーされた間隔に追加します。 、および対象セグメントを更新します。間隔の[L、R]値。
3.すべての間隔がカバーされるまで、ステップ2を繰り返します。画像の説明を追加してください
図2間隔カバレッジ
図2では、すべての線分が左端でソートされています。したがって、最初にセグメント1を選択し、次に2と3で長い方の3を選択します。4と5は必須ではなかったため、スキップされました。最終的な最適解は1と3です。

もう一度分析してみましょう

最適な負荷問題(通常のナップサック問題としても知られています)

アイテム説明:ポーションはn種類あり、いずれも体積がVで濃度が異なります。それらを混ぜ合わせて、w%以下の濃度のポーションを作ります。ポーションの最大量を得るためにそれをどのように混ぜますか?ポーションは、その一部だけでなく、まったく使用されていないか、まったく使用されていないことに注意してください。

この質問では、濃度がw%以下のポーションを構成する必要があります。
貪欲な戦略:濃度の低いポーションを探してみてください。
そのため、まずポーションを濃度の小さいものから大きいものへと並べ替え、ポーションの濃度がw%以下の場合はポーションを追加します。ポーションの濃度がw%より大きい場合は、混合後の総濃度を計算します。 w%以下の場合は追加し、そうでない場合は判断を終了します。

マルチマシンスケジューリングの問題

問題の説明:n個の独立したジョブがあり、それらはm個の同一のマシンによって処理されます。ジョブiの処理時間はtiであり、各ジョブは任意のマシンで処理できますが、中断または分割することはできません。可能な限り短い時間で、n個のジョブがm台のマシンによって処理および処理されるジョブスケジューリングスキームを提供する必要があります。

マルチマシンスケジューリング問題を解決するための貪欲な戦略は次のとおりです。処理時間が最も長いジョブが優先されます。つまり、処理時間が最も長いジョブが最初のアイドル状態のマシンに割り当てられます。長い処理ジョブに優先順位を付けて、全体として可能な限り短い処理時間を取得します。

1.n≤mの場合、必要な時間はn個のジョブの中で最も長い処理時間tです。

2. n> mの場合、最初にn個のジョブを処理時間の降順でソートしてから、アイドル状態のマシンに順番にジョブを割り当てます。

トピックの説明

XiaoMingは「コイントス」ゲームをプレイしています。

テーブルの上に一列に並べられたいくつかのコインがあります。ヘッドには*を使用し、テールにはoを使用します(ゼロではなく小文字)。

たとえば、次のようになります。oo * oooo;

左側の2枚のコインを同時に裏返すと、oooo***ooooになります。

Xiaomingの質問は、初期状態と到達する目標状態がわかっていて、同時に2つの隣接するコインしかフリップできない場合、特定の状況でのフリップの最小数はいくつですか?

隣接する2枚のコインを裏返すことをワンステップ操作と呼ぶことに同意しました。

説明を入力してください

等しい長さの文字列の2行は、それぞれ初期状態と達成される目標状態を表します。

各行の長さは<1000です。

出力の説明

操作ステップの最小数を表す整数。

入出力例

入力

**********
o****o****

出力

5

アイデア:最初に決定します。2つの隣接する文字を反転することによって文字列S1を別の文字列S2に変換できる場合、S1とS2は偶数の文字が異なる必要があります。次に、それが局所最適から全体最適までであるかどうか、貪欲のプロセスについて考えてください。左から始めて、最初の異なる文字(Zと呼びます)を見つけます。Zの左側の文字はすべて同じで、反転する必要はありません。Zの右側には偶数個の異なる文字が必要です。そして、Zを反転する必要があり、それを反転する必要があります。一度裏返すと、もう一度裏返す必要はありません。したがって、左から右に反転するプロセスでは、各反転が必要です。つまり、Zを反転する局所最適操作は、グローバル最適操作でもあります。だから貪欲は正しい。

s1 = list(input())
s2 = list(input())
ans = 0
for i in range(len(s1) - 1):
    if s1[i] != s2[i]:
        if s1[i + 1] == 'o': s1[i + 1] = '*'
        else:                s1[i + 1] = 'o'
#Python也有类似的三目运算,把上面2行换成:
#       s1[i + 1] = '*' if s1[i + 1] == 'o' else 'o'
        ans += 1
print(ans)

おすすめ

転載: blog.csdn.net/m0_51951121/article/details/122977961