动态规划算法:硬币的最大总和(小于或等于k)

Posted

技术标签:

【中文标题】动态规划算法:硬币的最大总和(小于或等于k)【英文标题】:Dynamic Programming Algorithm: Maximum sum of coins (less or equal to k) 【发布时间】:2015-02-04 15:52:13 【问题描述】:

给定一个包含 n 个硬币值的列表和一个总和 k,找到小于或等于 k ​​的最大可能总和。每个硬币可以根据需要多次使用。如果没有选择硬币,0 被认为是最大的。 我尝试解决这个问题,我知道这是动态编程,但我无法在小于 O(n^2) 的时间内解决这个问题。 这个问题的算法是什么?

【问题讨论】:

我认为您可以使用相同的coin change 算法。但这将是 O(nk) 除非 P=NP(很可能是 P!=NP),否则您无法在 O(n^2) 中解决它,因为这是子集和问题,即 NP Complete。整数的 DP 解决方案提供伪多项式 O(nk) 尝试将问题定义为“让我们将 f(___) 定义为遵守限制 ___ 的最大可能总和”。你可以用什么来代替___s? (这里的两个 ___ 可能不同。) @amit:有关系,但真的一样吗?很容易将这个问题转换为子集和——只需添加每个硬币 i 的 RoundUp(k/value[i]) 副本——但如何朝另一个方向发展并不明显,即如何表示一个 finite 在这个问题中的子集和问题中某个数的副本数。 @j_random_hacker Given a list of n coin values and a sum k, find the maximum possible sum which is less or equal to k. 这正是子集和的优化问题。细微的区别是您可以多次选择每个元素,这不会使问题变得更容易/更难。 【参考方案1】:

除非 P=NP(很可能是 P!=NP),否则您无法在 O(n^2) 中解决它,因为这是subset sum problem,即NP Complete。 DP solution for integers offers psedo-polynomialO(nk) 时间,这可能是你最好的选择。

在您的情况下,您可以多次选择一个元素,因此递归公式将是:

D(0,i) = true
D(x,0) = false   x!=0
D(x,i) = D(x-coin[i],i) OR D(x,i-1)
                     ^
          note i and not (i-1) here

您需要找到小于k 的最高x 使得D(x,n) = true,这相当容易 - 如果您构建一个大小为 (k+1)*(n+1) 的二维表 - 您需要找到最右边的列(让它成为x),这样D[x][n] = true,在最后一行的单个路径中很容易找到。

【讨论】:

第一个问题:D(x,i) 是否代表语句的真假值:可以从 i 个硬币中得到 sum x 吗? @user3245855 D(x,i) 表示语句的真假值:可以用硬币求和x吗c1,c2,...,ci【参考方案2】:
#include<iostream>
#include<stdlib.h>
#define INT_MAX 99999999
using namespace std;
int compare(const void *a,const void *b)

    return *(int*)a-*(int*)b;


struct res

    bool yes;
    int count;
;
struct res *t=new struct res [2000001];
//t=new struct res [1000001];
int fun(int arr[],int n,int k)

    int max=arr[n-1]*k;
    t[0].yes=true;
    t[0].count=0;
    int f;
    for(int i=1;i<max;i++)
    
        t[i].yes=false;
        t[i].count=INT_MAX;
        for(int j=0;j<n;j++)
        
            int val=arr[j];
            int cnt=1;
            f=0;
            while(val<=i && cnt<=k)
            
                if(t[i-val].yes==true)
                
                    t[i].yes=true;
                    t[i].count=min(t[i].count,cnt+t[i-val].count);
                    break;
                
                val+=arr[i];
                cnt++;
            
        
    
    for(int i=1;i<max;i++)
    
        if(t[i].count>k)
            return i-1;
    

int main()

    int t,k,n;
    int arr[50];
    cin>>t;
    int x=1;
    while(t--)
    
        cin>>k>>n;
        for(int i=0;i<n;i++)
            cin>>arr[i];
        qsort(arr,n,sizeof(int),compare);
        cout<<"Case #"<<x<<endl<<fun(arr,n,k)<<endl;
        x++; 
    

【讨论】:

任何解释都会有所帮助。

以上是关于动态规划算法:硬币的最大总和(小于或等于k)的主要内容,如果未能解决你的问题,请参考以下文章

动态规划—数字三角形

使用每一行的二维数组中小于或等于 k ​​的最大可能总和

动态规划的题目特点以及求“硬币个数最少”

查找最大索引 j 的有效算法,使得从索引 i 到 j 的总和小于 k

动态规划之拾取硬币

动态规划 硬币问题