15日目1371通貨システム(完全なナップサック問題)

1371.通貨システム

与えられたVVV通貨(単位:人民元)、各通貨の使用回数に制限はありません。

異なる通貨は同じ額面金額を持つ場合があります。

さて、このVVを使ってほしいV通貨はNNを構成しますN元、いくつかの異なる方法がありますか?

入力フォーマット
最初の行には2つの整数VVが含まれていますVNNN

次の数行で、合計VVが入力されますV整数、各整数は通貨の額面を表します。

出力形式
要求されたソリューションの総数を表す整数を出力します

データ範囲
1≤V≤251≤V≤251V2 5
1≤N≤100001≤N≤100001N1 0 0 0 0
答えはであることが保証されている長い長いですl o n g long long範囲内のL個のO 、N 、G

入力サンプル:

3 10
1 2 5

サンプル出力:

10

アイデア:DP分析

ここに画像の説明を挿入
ステータス表示: f[i][j]前のi通貨から選択されたjスキーム数を示し、その合計値は選択されたすべてのメソッドのセットを超えません

次にf[n][m]、前のn通貨から選択され、合計値が超えないmすべてのオプションのセット内のオプション数が答えであることを意味します。

セット分割:

セクションに従って、i通貨は01a、2a、3a ,,,,k、セグメント化されたコレクションの数を選択できますf[i][j]その中でk*w[i] <= j、つまり、バックパックをロードできる場合、列挙された最初のi通貨はいくつかを選択できます。

状態計算:

f[i][j] = f[i-1][j]+f[i-1][j-w[i]]+f[i-1][j-2*w[i]],,,,,,+f[i-1][j-k*w[i]]

Javaコード


import java.util.Scanner;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        //用n种货币凑成m元钱的方案数,方案数可能很大,故用long型
        long[][] dp = new long[n + 1][m + 1];
        //初始化,用0种货币凑成0元钱的方案数是1,其余dp[0][j]默认是0
        //即用0种货币凑成j(j!=0)元钱的方案数是0
        dp[0][0] = 1;

        for(int i = 1;i <= n;i++){
    
    
            int v = scanner.nextInt();//当前货币
            for(int j = 0;j <= m;j++){
    
    
                for(int k = 0; k*v <= j; k++){
    
    //当前货币使用0~k次
                    dp[i][j] += dp[i-1][j-k*v];
                }
            }
        }
        System.out.println(dp[n][m]);
    }
}

最適化を検討する

v最初のi記事のボリュームを表します(額面)

前のi通貨から選択すると、値を超えないj状態次のように表されます。
f[i][j] = f[i-1][j] + f[i-1][j-v]+f[i-1][j-2v]+,,,,,+f[i-1][j-kv])
前のi通貨から選択するとj-v、値を超えない状態は次のように表されます。
f[i][j-v] = f[i-1][j-v]+f[i-1][j-2v]+,,,,,+f[i-1][j-kv])

次の2つのタイプを減算します。

f[i][j] = f[i-1][j]+f[i][j-v])

Javaコード


import java.util.Scanner;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        //用n种货币凑成m元钱的方案数,方案数可能很大,故用long型
        long[][] dp = new long[n + 1][m + 1];
        //初始化,用0种货币凑成0元钱的方案数是1,其余dp[0][j]默认是0
        //即用0种货币凑成j(j!=0)元钱的方案数是0
        dp[0][0] = 1;

        for(int i = 1;i <= n;i++){
    
    
            int v = scanner.nextInt();//当前货币
            for(int j = 0;j <= m;j++){
    
    
               dp[i][j] = dp[i-1][j];
               if(j >= v) dp[i][j] += dp[i][j-v];
            }
        }
        System.out.println(dp[n][m]);
    }
}

最適化するために、継続検討と一次元DPになる
上から得られたf[i][j] = f[i-1][j]+f[i][j-v])。我々が発見f[i][j]アウターことforループはi最初のループ、および外側の値だけforループはi-1最初のように、ループ同等の変換により、上記の式は次のようになります。
f[j] = f[j] + f[j-v]

ではf[j] = f[j] + f[j-v]、なぜそれはf[i][j] = f[i-1][j]+f[i][j-v])等しいのですか?

  1. 等号の右側f[j]は、左側f[j]の現在のforループを更新するために使用されます。現在の外側のループが最初のiループであるのに対し、等号の右側f[j]前のforループ、つまり外側のforループで計算されることに注意してください。i - 1計算され、時間内f[j]保存されf[i-1][j]ます。つまり、です
  2. 等号の右側にあるf[j-v]forは、内側のループで小さいものから大きいものに更新するためであり、それj-vよりも小さくする必要があるjため外側のループがシークするf[j]の最初のループであったときの値にf[j-v]計算および更新されています。 、つまりでは、なぜ上記が同等なのですforif[i][j-v]f[j] = f[j] + f[j-v]f[i][j] = f[i-1][j]+f[i][j-v])

Javaコード


import java.util.Scanner;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        //用n种货币凑成m元钱的方案数,方案数可能很大,故用long型
        long[] dp = new long[m + 1];
        //初始化,用0种货币凑成0元钱的方案数是1,其余dp[0][j]默认是0
        //即用0种货币凑成j(j!=0)元钱的方案数是0
        dp[0] = 1;

        /*for(int i = 1;i <= n;i++){
            int v = scanner.nextInt();//当前货币
            for(int j = 0;j <= m;j++){
            //内层for循环中就一个if语句,我们发现if语句中的条件还可以提到for中,故还可简化为下面的写法
               if(j >= v) dp[j] += dp[j-v]; 
            }*/
        for(int i = 1;i <= n;i++){
    
    
            int v = scanner.nextInt();//当前货币
            for(int j = v;j <= m;j++){
    
    
               dp[j] += dp[j-v];
            }
        }
        System.out.println(dp[m]);
    }
}

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/YouMing_Li/article/details/113869701