C++---ナップザック モデル---通貨システム 2 (デイリー ワン アルゴリズム 2023.3.22)

注:
この質問は、「dp 動的プログラミング - 完全なナップサック」「ナップサック モデル - 通貨システム 1」の拡張質問です。最初にこれら 2 つを理解することをお勧めします。これにより、この質問はより役に立ちます。

質問:
ネットユーザーの国には額面の異なる n 種類の通貨があり、i 番目の通貨の額面は a[i] ですが、各通貨の数は無限にあると仮定できます。

便宜上、n 種類の通貨と額面配列 a[1…n] を含む通貨システムを (n,a) と表します。

完全な通貨システムでは、各非負整数の量 x を表現できる必要があります。つまり、各非負整数 x に対して、a[i]× を満たす n 個の非負整数 t[i] が存在します。 t [i] の合計は x です。

しかし、ネットユーザーの国では、通貨制度が不完全である可能性があります。つまり、通貨制度では表現できない金額 x が存在する可能性があります。

たとえば、通貨システム n=3、a=[2,5,9] では、金額 1,3 を表すことができません。

2 つの通貨体系 (n,a) と (m,b) は、非負の整数 x が両方の通貨体系で表現できるか、どちらの out でも表現できない場合にのみ等価です。

現在、ネチズンは通貨システムの簡素化を計画しています。

彼らは、(m,b) が元の通貨システム (n,a) と同等であり、m ができるだけ小さいような通貨システム (m,b) を見つけたいと考えています。

彼らは、最小の m を見つけるというこの難しいタスクを手伝ってほしいと考えています。

入力形式
入力ファイルの最初の行には、データ セットの数を表す整数 T が含まれています。
次に、T 個のデータを次の形式で与えます。 
データの各セットの最初の行には、正の整数 n が含まれます。
次の行には、スペースで区切られた n 個の正の整数 a[i] が含まれています。

出力形式
出力ファイルは合計 T 行あり、データセットごとに、(n,a) に相当するすべての通貨系 (m,b) の中で最小の m を表す正の整数を含む行を出力します。

データ範囲
1≤n≤100、1≤a
[i]
≤25000、1≤T≤20

输入:
2 
4 
3 19 10 6 
5 
11 29 13 19 17 
输出:
2
5
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 110, M = 25010;
int t;              //t组数据
int v[N], f[M];     //v[i]第i个钞票的面值

int main() {
    
    
    cin >> t;
    while (t--) {
    
    
        int n, m = 0;   //n是当前组不同面值钞票的数量,m为当前组中面额最大的钞票
        cin >> n;

        for (int i = 1; i<=n; i++) {
    
            //读入
            cin >> v[i];
            m = max(m, v[i]);
        }
        
        memset(f, 0, sizeof f); //要多次计算dp,所以记得f要归零
        f[0] = 1; //这里还是一样,在0种钞票中选总和为0的,是一种方案                      
        //一维双循环完全背包dp,不理解可以去看完全背包那篇文章,有详解
        for (int i = 1; i<=n; i++) {
    
    
            for (int j = v[i]; j<=m; j++) {
    
    
                f[j] += f[j-v[i]];
            }
        }
        
        //求出答案,只有当v[i]不能被其他方案表示出来时(例如v={2,4,6,9},其中6可以被2*3或2+4表示出来, 而9无法被表示出来),
        //说明当前v[i]是不能被取代的,需要加入答案组中
        int res = 0;
        for (int i = 1; i<=n; i++) {
    
    
            if (f[v[i]] == 1) res++;
        }
        cout << res << endl;
    }

    return 0;
}

アイデア:
トピックはさらに複雑です。まず質問を分析してから、栗を出しましょう。

このトピックの主な条件:
1. n 種類の通貨と額面配列 a[1…n] を持つ通貨システムは (n,a) として示されます。
2. 2 つの通貨システム (n,a) と (m,b) は同等です。負でない整数 x が両方の通貨体系で表現できるか、どちらの通貨体系でも表現できない場合に限ります。 
3. (m,b) が元の通貨システム (n,a) と同等であり、m ができるだけ小さいという条件を満たす通貨システム (m,b) を見つけたいと考えています。

例:

v = {
    
    3,19,10,6}
res = {
    
    }

3	不能被v中的其他元素表示出来,是必要的元素,res = {
    
    3},
19	可以被10+3*3表示出来,不添加进res,
10	不能被表示出来,res = {
    
    3, 10},
6	可以被3+3表示出来,不添加进res。

结果(m)为2。

発見プロパティ:
プロパティ 1: a1、a2...an は b の要素で表現できなければなりません
プロパティ 2: 最適解では、b1、b2...bm は a から選択される必要があります
プロパティ 3: b1、b2 ...bm 他の bi で表すことはできません

結論:
上記に基づいて、これらのプロパティに従って、データの各セットは完全なナップザック問題に変換できます。必要なのは、各宗派がいくつのスキームを表現できるかを調べてから、For 内の要素をループすることだけです。比較すると、スキームが 1 つ以上ある場合 (自分自身をスキームとみなします)、それは他の宗派で表すことができることを意味し、1 に等しいスキームの数を b に代入し、最終的に b の要素の数を次のように求めます。答え。

古典的な y スタイルの dp メソッドである dp についてもう一度触れておきます。
1. 状態の表現:
f[i][j]: 最初の i 種類の紙幣の場合、合計は正確に j で、属性は Count です。
2. 状態の計算:
完全なバックパックのアイデアに従い、i 番目の項目を取得して判断します: (0 から開始)
f[i][j] += sum(f[i-1][j], f[i-1][j-1v], f[i-1][j-2v], ..., f[i-1][j-sv])

参考になったら、無料の「いいね!」をお願いします〜 誰かが見てくれると、書く応援のモチベーションになります!

免責事項:
アルゴリズムのアイデアの出典はY氏です。詳しくはhttps://www.acwing.com/をご参照ください。
この記事は学習記録と交流のみに使用されます。

おすすめ

転載: blog.csdn.net/SRestia/article/details/129705702