给定n个硬币值和和k的列表,找到小于或等于k的最大可能总和。每个硬币可根据需要多次使用。如果没有选择硬币,则认为0是最大的。 我尝试解决这个问题,我知道这是动态编程,但我不能在小于O(n ^ 2)的情况下解决这个问题。 这个问题的算法是什么?
答案 0 :(得分:1)
你不能在O(n ^ 2)中解决它,除非P = NP(很可能是P!= NP),因为这是subset sum problem,即NP Complete。 DP 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++;
}
}