动态编程算法:最大硬币总和(小于或等于k)

时间:2015-02-04 15:52:13

标签: algorithm dynamic-programming

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

2 个答案:

答案 0 :(得分:1)

你不能在O(n ^ 2)中解决它,除非P = NP(很可能是P!= NP),因为这是subset sum problem,即NP CompleteDP solution for integers offers psedo-polynomial O(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 + 1)*(n的二维表),你需要找到小于x的最高k,这样D(x,n) = true就相当容易了。 +1) - 您需要找到最合适的列(让它为x),以便D[x][n] = true,这在最后一行的单个路径中相当容易找到。

答案 1 :(得分:0)

#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++; 
    }
}