C++ -- 動的プログラミングのナップザック問題 (1)

1. [テンプレート] 01 バックパック_Niuke Topic_Niuke.com

最大容積が V のナップザックを持っています。

n 個の項目があり、i 番目の項目の体積は vivi 、値は wiwi です。

(1) このバックパックに収納できるアイテムの最大値を見つけますか?

(2) ナップザックがちょうど満杯の場合、最大でいくらの価値を保持できますか?

説明を入力してください:

最初の行の 2 つの整数 n と V は、アイテムの数とバックパックの体積を表します。

次の n 行は、それぞれ 2 つの数字 vivi と wiwi を持ち、i 番目の項目の量と値を表します。

1≤n,V,vi,wi≤10001≤n,V,vi,wi≤1000

出力の説明:

出力は2行あり、1行目は1問目の答え、2行目は2問目の答えを出力します。解がない場合は0を出力してください。

例1

入力:

3 5 
2 10 
4 5 
1 4

出力:

14 
9

例証します:

合計値は 1 つ目と 3 つ目のアイテムを積むと最大になりますが、2 つ目と 3 つ目のアイテムを詰めると、ナップザックがぴったりいっぱいになり、合計値が最大になります。

例 2

入力:

3 8 
12 6 
11 8 
6 8

出力:

8 
0

例証します:

3 番目のアイテムをロードすると、合計値は最大になりますが満たされず、バックパックを埋める解決策がありません。

分析: ナップザック問題の場合、この質問は非常に重要です。分析プロセスは次の図に示されています。

#include <iostream>
#include <string.h>
#include <vector>
#include <stdio.h>
using namespace std;
const int V=1010;//体积
int n=0;
int v=0;
int num[V];//体积
int val[V];//价值
int main()
{
    cin>>n;
    cin>>v;
    for(int i=0;i<n;i++)
    {      
        cin>>num[i]>>val[i];
    }   
    int dp[V];
    //第一题
    memset(dp,0,sizeof dp);//初始化
    for(int i=1;i<=n;i++)
    {
        for(int j=v;j>=num[i-1];j--)
        {
            dp[j] = max(dp[j],dp[j - num[i-1]] + val[i-1]);
        }
    }
    cout<<dp[v]<<endl;
    //第二题
    memset(dp,0,sizeof dp);
    for(int j = 1; j <= v; j++)
        dp[j] = -1;
    for(int i=1;i<=n;i++)
    {
        for(int j=v;j>=num[i-1];j--)
        {
            
            if(dp[j - num[i-1]] != -1)
                dp[j] = max(dp[j],dp[j - num[i-1]] + val[i-1]);
        }
    }
    cout << (dp[v] == -1 ? 0 : dp[v]) << endl;
    return 0;
}

2. セグメンテーションとサブセット  LeetCode 公式ウェブサイト - 世界中のオタクに愛されるテクノロジー成長プラットフォーム

正の整数のみを含む空ではない配列 が与えられますnumsこの配列を 2 つの部分集合の要素の合計が等しくなるように 2 つの部分集合に分割できるかどうかを判断してください。

例 1:

入力: nums = [1,5,11,5]
出力: true
説明:配列は [1, 5, 5] と [11] に分割できます。

例 2:

入力: nums = [1,2,3,5]
出力: false
説明:配列を 2 要素の等しいサブセットに分割することはできません。

分析: この質問の場合、解決する必要がある問題は、それをナップザック問題に変換する方法です。この質問では、2 つの部分配列のサイズが等しくなるように配列を分割する必要があるため、配列の合計を求めることができます。配列要素を取得し、それを 2 で割って判断します。割り切れない場合は false を返します。2 で割り切れる場合は、ナップザック問題が適用されます。

class Solution {
public:
    bool canPartition(vector<int>& nums) 
    {
        int n=nums.size();
        int sum=0;
        for(const auto& s:nums) sum+=s;
        
        if(sum%2==1)
            return false;
        sum=sum/2;
        //初始化
        vector<bool> dp(sum+1);

        dp[0]=true;

        for(int i=1;i<=n;i++)
        {
            for(int j=sum;j>=nums[i-1];j--)
            {
                    dp[j]=dp[j]||dp[j-nums[i-1]];              
            }
        }
        return dp[sum];
    }
};

3. Goal と  LeetCode 公式ウェブサイト - 世界中のオタクに愛されるテクノロジー成長プラットフォーム

負でない整数の配列numsと integer が与えられますtarget

配列内の各整数に '+'または を追加し'-'、すべての整数を連結すると、式を作成できます。

  • たとえば、nums = [2, 1]2の前に追加し'+'1の前に追加して'-'から連結して、式 を取得できます"+2-1"

上記のメソッドによって構築でき、 と等しいと評価される個別のtargetの数を返します

例 1:

入力: nums = [1,1,1,1,1]、ターゲット = 3
出力: 5
説明:最終的なターゲットの合計を 3 にする方法は 5 つあります。
-1 + 1 + 1 + 1 + 1 = 3 
+1 - 1 + 1 + 1 + 1 = 3 
+1 + 1 - 1 + 1 + 1 = 3 
+1 + 1 + 1 - 1 + 1 = 3 
+1 + 1 + 1 + 1 - 1 = 3

例 2:

入力: nums = [1]、ターゲット = 1
出力: 1

分析します:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) 
    {
        int sum=0;
        int n=nums.size();
        //转化为背包问题
        for(int i=0;i<n;i++) sum+=nums[i];
        if((sum+target)<0||(sum+target)%2) return 0;

        sum=(sum+target)/2;
        //初始化
        vector<int> dp(sum+1);
        dp[0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=sum;j>=nums[i-1];j--)
            {
                 dp[j]+=dp[j-nums[i-1]];
            }
        }
        return dp[sum];
        
    }
};

4. 最後の石の重さ (2)  LeetCode 公式ウェブサイト - 世界のオタクに愛されるテクノロジー成長プラットフォーム

整数の配列で表される石の山があります stones最初の石の重さはどこに stones[i]ありますか。i

各ターン、その中から任意の 2 つの石を選択し、それらを一緒に叩きます。石の重さが それぞれ と で xある と仮定しますこの場合、破砕によって考えられる結果は次のとおりです。yx <= y

  • の場合 x == y、両方の石は完全に粉砕されています。
  • の場合 x != y、 x 重量の石は完全に粉砕され、 y 重量の石は新しい重量 になります y-x

最終的に残る石はせいぜい1つだけだこの石の可能な最小の重量を返します石がなくなった場合は戻ります0

例 1:

入力: stone = [2,7,4,1,8,1]
出力: 1
説明: 
2 と 4 を結合すると 2 が得られるため、配列は [2,7,1,8,1] に変換され、
7 が結合されます。と 8 、1 を取得、配列は [2,1,1,1] に変換され、
2 と 1 を結合して 1 を取得、配列は [1,1,1] に変換され、
1 と 1 を結合して取得0 であるため、配列は最適値である [1] に変換されます。

例 2:

入力:石 = [31,26,33,21,40]
出力: 5

分析します:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) 
    {
        int sum=0;
        int n=stones.size();
        for(int i=0;i<n;i++) sum+=stones[i];
        int enquesum=sum;
        sum=sum/2;
        vector<int> dp(sum+1);
        for(int i=1;i<=n;i++)
        {
            for(int j=sum;j>=stones[i-1];j--)
            {
                dp[j]=max(dp[j],dp[j-stones[i-1]]+stones[i-1]);
            }
        }
        return enquesum-2*dp[sum];
    }
};

おすすめ

転載: blog.csdn.net/weixin_66828150/article/details/132568265