DPアルゴリズム - POJ 4119:複雑な整数の除算の問題

タイトル

合計時間制限:メモリ制限を200ミリ秒:65536kBは
説明
= N1 + N2 + ...正の整数を、nは正の整数の系列を表し、N + NK、 N1> = N2> = ...> = NK> = 1、K> = 1
これは、正の整数を表す。nは正の整数であり、nはパーティションと呼ばれています。

入力
テストデータの複数のセットを含む標準入力。各試験は、2つの整数を含む、入力データのラインであるNとK.
(0 <N <= 50、 0 <K <= N)
出力
各試験のために、データの出力の次の3行:
最初の行:Nは正の整数の数Kとに分割され
、N個の分割:第二行番号は、いくつかの異なる正の整数とに分割されている
第三の行:Nは奇数及び正の整数の分割数に分割される
サンプル入力
52
出力例
2
。3
。3
ヒント
最初のライン:4 + 1,3 +図2に示すように、
第二のライン:5,4 + 1,3 + 2
行3:1 + 5,1 + 3,1 + 1 + 1 + 1 + 1 + 1

まず尋ねました

:制約K前問題よりも整数分割問題は、この問題は、ナップザック問題に類推することができ、問題を次のように変換される
K番号[1 ... N]を選択し、Nである、から選択されます数は、典型的な動的プログラミング問題であり、繰り返すことができます。
[I 1 ...]選択された数Qから、次に下位問題を考慮し、J.ある
注文F [I] [J] [ Q] ソリューション下位問題図。
次の境界条件を考える:1である[1 ... 1]から数、グループFから選択される唯一の方法を選択する[1] [1] [ 1] = 1;
次の状態遷移を考える:
Fを[I] [J] [Q] = F [I-1] [J] [Q] + F [I] [J - I] [Q - 1]
[I-1] F [j]が[Q] I THを表します数値は、その後のQ番号を選択し、選択されていないI-1、およびJで前
F [i]が- [Q-1]は、i番目の選択は、フロント選択された数のI Q-1を示し、[JはI]を、かつiはI-1ないJI、ある質問サブIで使用されるために取られる、すなわち各選択された数は、一意でない
ことが一般的なナップザック問題の特別なケースです。
このように、記録ダイナミックな計画プロセスに3桁のグループを使用して、この問題の解決をすることができますが、いくつかの計画は、動的配列は、この道をすることができ、スペースを節約するためにスクロールするために使用することができます。
最初のサイクルは、I、F [J] [Q]が選択されたI-1フロントの数を表す開始前に前のサイクルの層と2桁fの群[J] [Q]は、iは、選択された数を表すと仮定するFため、Q jを構成する[I] [JI] [ Q-1]の
ためのトラバースF前に[I] [J] [ Q] に、F [JI]値[Q-1]で:i番目のサイクルは、状態遷移がに簡略化することができるように、更新された値である
[-Q 1] F [J] [Q] + = F [JI】
次のようにコードのこの部分は:

dp[0][0] = 1;
for (int i = 1; i <= n; i++)
    for (int j = i; j <= n; j++)
	for (int q = k; q >= 1; q--)
	    dp[j][q] += dp[j - i][q - 1];
printf("%d\n", dp[n][k]);

2番目の質問

単純なデジタル整数除算問題の増加前に第二の問題は、状態遷移方程式に反映される限度、内部で繰り返すことができない
セットF [I] [j]は、以前のハッシュI jの数、示し
た状態遷移式:
F [I] [J] = F [I-1] [JI] + F [I-1] [j]は
同様に、二次元アレイに留意すべき寸法にスクロールすることができるトラバース降順でJ
コード:

dp1[0] = 1;
        for (int i = 1; i <= n; i++)
            for (int j = n; j >= i; j--)
                dp1[j] += dp1[j - i];
        printf("%d\n", dp1[n]);

3番目の質問

唯一の正の奇数、ノートを選択することができ整数の除算の問題では、単純な増加よりも3番目の質問の後、デジタルスチルこの事実を理解し、繰り返し、最初の二つの質問は、書くことは非常に良いですが
、状態遷移方程式を書くために直接:
F [ I] [J] = F [I]、[J - I] + F [I-2] [J]
F [1] [1] = 1。。。
iが奇数のトラバーサルを取り、次いで、アレイを圧延することにより、jはトラバースに小から大まで必要。
コード:

dp2[0] = 1;
        for (int i = 1; i <= n; i += 2)
            for (int j = i; j <= n; j++)
                dp2[j] += dp2[j - i];
        printf("%d\n", dp2[n]);

コード全体の質問:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

typedef long long ll;

const int N = 50 + 5;
int dp[N][N], dp1[N], dp2[N];

int main() {
    for (int n, k; scanf("%d%d", &n, &k) == 2;) {
        memset(dp, 0, sizeof(dp));
        memset(dp1, 0, sizeof(dp1));
        memset(dp2, 0, sizeof(dp2));
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++)
            for (int j = i; j <= n; j++)
                for (int q = k; q >= 1; q--)
                    dp[j][q] += dp[j - i][q - 1];
        printf("%d\n", dp[n][k]);
        dp1[0] = 1;
        for (int i = 1; i <= n; i++)
            for (int j = n; j >= i; j--)
                dp1[j] += dp1[j - i];
        printf("%d\n", dp1[n]);
        dp2[0] = 1;
        for (int i = 1; i <= n; i += 2)
            for (int j = i; j <= n; j++)
                dp2[j] += dp2[j - i];
        printf("%d\n", dp2[n]);
    }
    return 0;
}

参照コード:https://blog.csdn.net/Nightmare_ak/article/details/94414363

おすすめ

転載: www.cnblogs.com/zhangyue123/p/12557503.html