6

あなたはいくつかのレゴプラスチックレンガを持っています、すべてのレンガは1x1x1です。また、1xN(N <= 80)のタイルがあり、その上にLEGOブロックを配置する必要があります。それらを順番に並べることができます(3つ以上の連続したブリックがある場合は1つのシーケンスが正しいです)。また、2つのシーケンスの間に少なくとも1つの空きスペースが必要です。タイルにレンガを配置できるさまざまな組み合わせの数を計算する必要があります。

次に例を示します。

タイルが1x7の場合、17の異なる組み合わせがあります。

入力:7出力:17

例の写真
(出典:mendo.mk

また、レンガがない場合は、1つの組み合わせとしてカウントされます。

私はこの問題に取り組み、タイルの最大長が14(3シーケンス)である場合の可能な組み合わせを計算する方法を見つけました。forループを使用していることがわかりました。

私の最大の問題は、実行する必要のあるforループの数が非常に多いことです。たとえば、1つのシーケンスの場合は1 forループを使用し、2つのシーケンスの場合は2つのループ+ 1の1つのシーケンスを使用します...したがって、80のブロックすべてを使用する場合、20のシーケンスを作成でき、210を超えるforループを使用する必要があります。膨大な数。だから、1つに入れ子にできればいいですね。試してみましたが、めちゃくちゃになって正解しません。

新しいコード:

#include <iostream>
using namespace std;
int main()
{
    long long int places, combinations = 1;
    cin >> places;
    long long int f[80], g[80];
    f[0] = 0;
    f[1] = 0;
    f[2] = 0;
    g[0] = 1;
    g[1] = 1;
    g[2] = 1;
    for(int i = 3; i<=places; i++)
    {
        f[i] = f[i-1] + g[i-3];
        g[i] = f[i-1] + g[i-1];
    }
    combinations = f[places] + g[places];
    cout << combinations;
    return 0;
}
4

2 に答える 2

10

これがカウントの問題である場合(組み合わせを出力するのではなく、単にカウントするだけ)、簡単な問題です。ここでn≥3で解いて、n + 1で解くと仮定すると、誘導によって解きます。

仮定fは、最後のアイテムがレンガになるように可能な方法の数を示す関数です。同様gに、最後のアイテムがレンガではないように可能な方法の数を示す関数です。h = f+g、をすべての可能な方法の数として定義しましょう。

だから私たちは持っています:

f(n+1) = f(n) + g(n-2)
g(n+1) = g(n) + f(n)

初期状態:

for n=0,1,2: g=1, f= 0.
for n = 3: g=1,f=1

サンプル:

n=4: g=2,f=2 ==> h=4
n=5: g=4, f= 3 ==> h=7
n=6: g=7, f= 4 ==> h=11
n=7: g=11,f=6 ==> h=17

のforループで解決できますO(n)


なぜ:

f(n+1) = f(n) + g(n-2)
g(n+1) = g(n) + f(n)

まず、最初の部分を証明しましょう:

f(n)は最後の項目にプラスチックのレンガがある作業ソリューションであり、g(n)は最後の項目にレンガがない作業ソリューションであると仮定したことを思い出してください。

f(n + 1)は、最後に1つのブリックを追加することでf(n)から取得できます。また、f(n + 1)は、g(n-2)の後に3つのブリックを追加することで取得できます。これは、n-1、n、n+1のセルを意味します。

g(n-1)またはg(n)の後にブリックを追加して、f(n + 1)の有効なソリューションを作成することはできません。これらは有効なソリューションではないためです(連続するブリックの数は3未満です)。また、以前はf(n)によって列挙されていたため、g(n-3)の後にブリックを追加することによって発生するウェイの数を数える必要がないことに注意してください。だから私たちは持っていf(n+1) = f(n) + g(n-2)ます。

g(n+1) = f(n)+g(n)同様に、g(n + 1)は、までの任意の有効なソリューションから簡単に作成できるため、このケースがより簡単であることを証明できます。nここには3つの連続するブリックバリアがないため、有効なソリューションの後に来ることができます。

于 2012-05-01T10:48:41.433 に答える
5

CSではなく数学のトレーニングを受けている人として、Saeed Amiriのアルゴリズムは非常に優れており、おそらくNから数百万まで(もちろん一定のメモリを使用)で十分に高速に動作しますが、時間の観点からより良いアルゴリズム。

私は彼が去ったところを拾います:

f(n+1) = f(n) + g(n-2)
g(n+1) = f(n) + g(n)

fとgは離散関数なので、シーケンスとして扱うことができます。これは、漸化式の線形システムになります。幸いなことに、このようなシステムは完全に解くことができるので、fとgの明示的な形式を提示することができます。
残念ながら、SOはmath.SEのようにMathJaxをサポートしていないようです。そのため、これからは方程式の質が低いことをお詫びします。
させて

     | f(n)|
     | f(n-1)|
u(n)= | f(n-2)|
     | g(n)|
     | g(n-1)|
     | g(n-2)|

つまり、u(n)はベクトル行です。次に、次のことが当てはまります。

| f(n + 1)| | 1 0 0 0 0 1 | | f(n)|
| f(n)| | 1 0 0 0 0 0 | | f(n-1)|
| f(n-1)| = | 0 1 0 0 0 0 | 。| f(n-2)|
| g(n + 1)| | 1 0 0 1 0 0 | | g(n)|
| g(n)| | 0 0 0 1 0 0 | | g(n-1)|
| g(n-1)| | 0 0 0 0 1 0 | | g(n-2)|

これから続くのは、それですu(n) = A * u(n-1)。ここで、Aは上の行列です。
次に、、u(n) = (A^(n-2)) * u(2)u(2)、問題の初期値を含むベクトルです。これにより、高速なべき乗を使用して計算し、それをに乗算O(log(n))できるため、複雑なアルゴリズムが得られます。(A^(n-2))u(2)

もちろん、そのような手法では、オーバーフローがほぼ保証されるため、おそらく何らかのBigIntが必要になります。

また、この手法はさらに一歩適用できることに注意してください
。Aの固有ベクトルと固有値を見つけて、固有ベクトルに分解することができますu(2)。次に、f(n)とg(n)の両方の閉じた形になります。

閉じた形式に基づくアルゴリズムに反対することを強くお勧めします
。これには、プログラミングの観点からは少なくともこの複雑さであり、(すべての固有値が整数である可能性が非常に低い場合を除いて)高精度の浮動小数点計算が含まれることはほぼ間違いありません。通常、定時演算ではありません。もちろん、どちらもBigInt操作ではありません。O(log(n))したがって、定数時間アルゴリズムは一般的に実行可能ではありません。さらに、ほとんどの用途では線形で十分であるため、おそらくは必要ありません。


ここで説明する手法は、さまざまな問題で使用でき、動的最適化問題で非常に役立ちます。さらに、通常、これを初めて見たとき、人々はかなり感銘を受けます;)

于 2012-05-02T12:46:06.377 に答える