题意 给你n种钱币,每种面值Ai,数量Ci,问你能凑成的不超过m的面值数量
思路1 这是我起初的想法,优化了半天还是超时了...不过这里还是说一下,有些想法今后可能还会用到。
完全仿照多重背包的求解,把Ci拆分成一些2的次幂加和的形式,具体可参看背包九讲~但这样复杂度多出了一个logC,就会超时...然后我想m种面值不可能都达到,因此我每次只遍历已经达到了面值,然后更新出来的新值加入到我的数组中...应该优化了不少的常数,但是还是不行....
思路2 思路1有些被固有的多重背包解法束缚了,其实这里有一个很大的变化,就是dp[i][j]只需保存bool值就行了,浪费了可以记录的量。利用保存的值,这道题就很类似于完全背包了,那么可以用完全背包的递推式优化~这样将将不超时~
状态:dp[i][j] 表示前i种钱币,达到这个j这种面值,还会剩下多少张i这种钱币
定义了状态转移就很好写了~就是注意还要利用滚动数组的思想,去掉i这一维~转移前见代码
初值就是 dp[i][0] = c[i]
实现思路2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
int a[110];
int c[110];
int dp[110000];
int n,m;
int main()
{
int i,j;
while(scanf("%d%d",&n,&m) && n)
{
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(i=1;i<=n;i++)
{
scanf("%d",&c[i]);
}
memset(dp,-1,sizeof(dp));
for(i=1;i<=n;i++)
{
dp[0] = c[i];
for(j=1;j<=m;j++)
{
if(dp[j] == -1 && (j - a[i] < 0 || dp[j-a[i]] == -1 || dp[j-a[i]] == 0))
{
continue;
}
if(dp[j] == -1)
{
dp[j] = dp[j-a[i]] - 1;
}
else
{
dp[j] = c[i];
}
}
}
int ans = 0;
for(i=1;i<=m;i++)
ans += dp[i] == -1? 0 : 1;
cout<<ans<<endl;
}
}
实现思路1(超时):
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
//vector<int> vec;
//vector<int> s;
int vec[1100];
int s[110000];
int vecnum,snum;
int a[110],c[110];
int n,m;
int dp[110000];
void ChaiFen(int x,int y)
{
int tmp = y;
int sum = 0,i = 0;
int tmp2;
while(tmp >= (1<<i))
{
tmp2 = x * (1<<i);
sum += tmp2;
//vec.push_back(a*(1<<i));
vec[vecnum++] = tmp2;
i++;
tmp -= (1<<i);
// cout<<vec[vecnum-1]<<endl;
}
if(y*x - sum > 0)
{
vec[vecnum++] = (y*x-sum);
// cout<<vec[vecnum-1]<<endl;
}
//cout<<endl;
}
bool cmp(int x,int y)
{
return x>y;
}
int main()
{
int i,j;
//s.reserve(110000);
//vec.reserve(2000);
while(scanf("%d%d",&n,&m)==2&&n)
{
//vec.clear();
//s.clear();
vecnum = 0;
snum = 0;
memset(dp,0,sizeof(dp));
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
for(i=0;i<n;i++)
{
scanf("%d",&c[i]);
}
for(i=0;i<n;i++)
{
ChaiFen(a[i],c[i]);
}
n = vecnum;
// cout<<n<<endl<<endl;
sort(vec,vec+vecnum,cmp);
//方便dp
s[snum++] = 0;
dp[0] = 1;
for(i=0;i<n;i++)
{
//因为后面要改变s的长度,所以要保存一下
int now = snum;
//cout<<"i="<<i<<endl;
for(j=0;j<now;j++)
{
int tmp = s[j] + vec[i];
if(tmp > m || dp[tmp] == 1)
continue;
dp[tmp] = 1;
s[snum++] = tmp;
//cout<<tmp<<endl;
}
//cout<<endl;
}
//cout<<s.size()-1<<"\n";
printf("%d\n",snum-1);
}
return 0;
}