アルゴリズム学習 - ナップザック問題の 0-1 ナップザック

最近、Likou バックパックのトピックの利益計画を立てようとしていたときにイライラしました。. . そして、この質問は、ナップザックの問題の 1 つについて考えるきっかけにもなりました。現在、Likou のナップザック問題の解の数は厳密に選択されており、最も多く選択されたものはありません。たとえば、タイトルを締める場合、目標の合計は、特定の値に到達したすべての計画の数です。そして利益計画の質問をしているときに考えたので、自分で考えて質問を作成しました。あなたも自分でやってみることができます~~~

配列 nums を与え、次に int 数 n を与え、配列から任意の数を取得するように要求しますが、取得した数値の合計が指定された n より小さい場合、各数値は一度しか取得できません。は解決策です.すべての計画の数は何ですか? たとえば、nums=[1,2,3] n=6 の場合、すべてのスキームは { {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3 }、{1、2、3}} 7種類。n=5 の場合、スキームの数は 6 です。

自分で考えて実行することもできます。ここに自分のアイデアとコードを投稿してください。

これも典型的な 0-1 ナップザック問題です. まず, ここでは動的計画法を用います. まず, 二次元配列 dp[i,j] を定義します. この配列は配列であり、制限値は j です プログラムの総数。ここには 2 つの状況があります。1 つ目は、nums[i-1]>limit の場合です。これは、この数を入れることができないことを意味するため、プランの数は dp[i-1, limit] と同じにする必要があります。最初の式を使用すると、次のことができます。 

dp[i,limit] =dp[i-1,limit];

2 番目のケース: nums[i-1]<=limit の場合、この数をスキームの数に追加できることを意味します. ここでは、デフォルトの dp[i,j]=1; これは、それ自体の数、方程式は次のとおりです。

dp[i,j]+=dp[i-1,j]+dp[i-1,j-nums[i-1]];

この方程式の意味は、このラウンドで j に制限された解の数に、前のラウンドで j に制限された解の数と、前のラウンドで j-nums[i-1] に制限された解の数を足したものです。これはラウンドの nums[i-1] を入れることができるので、入れなかったプランの数、つまり dp[i-1,j] と、入れた後のプランの数を含み、 put 入力後、j の上限値、つまり dp[i-1,j-nums[i-1]] を超えることはできないため、(i を固定した場合、j が大きいほうのラウンド数は、より小さい j )。

私のコードをここに貼り付けます:

        /// <summary>
        /// 有上限的方案数
        /// </summary>
        /// <param name="nums"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        public static int LimitSchemes(int[] nums, int limit)
        {
           int n = nums.Length;
            int[,] dp = new int[n + 1, limit + 1];
            for (int i = 1; i <= n; i++)
            {
                if (nums[i - 1] > limit)
                {
                    dp[i, limit] = dp[i - 1, limit];
                }
                for (int j = nums[i - 1]; j <= limit; j++)
                {
                    dp[i, j] = 1;
                    dp[i, j] += dp[i - 1, j] + dp[i - 1, j - nums[i - 1]];
                }
            }
            return dp[n, limit];
         }

この時点で、コードにはまだ最適化の余地があることがわかります. スペースを 1 次元削減し、ローリング配列を使用できます. しかし、一般的な考え方は同じままなので、以下にコードを掲載します。

 public static int LimitSchemes(int[] nums, int limit)
        {
           
            int n = nums.Length;
            int[] dp = new int[limit + 1];
            for (int i = 0; i < n; i++)
            {
                for (int j = limit; j >= nums[i]; j--)
                {
                    dp[j] += 1 + dp[j - nums[i]];//这里的1就是仅包含自己的那一种方案数
                }
            }
            return dp[limit];
        }

おすすめ

転載: blog.csdn.net/X1iaoXu666/article/details/124624283