题意是给一个数,给多组钱的数目和钱的面值,问能组成最大的小于那个数的值是多少。
就是背包,每次更新一下dp[i] = dp[i - temp] + temp就可以了(把价值函数和体积都用temp来表示)
但是单纯的dp这道题是会超时的,必须要进行一下优化。
每个枚数数都可以1,2,2^2....2^k-1,M + 1 - 2^k
最后一个数必须大于0,也就是说k的范围在0到log2 M+1 之间。
这些数的和可以调整0,1系数可以覆盖这个1到枚数之间所有的数,不会证明,但是感觉上也是那么回事。
之后把体积和价值合并一下,把一枚一枚散的乘上系数。化成k种货币,面值和体积都是乘上系数之后的。
把货币面值体积变换完之后正常的背包就可以了。
--------
再把枚举的思路提一下。准备一个所给数目的数组记录这个数用没用过。
再准备一个数组记录能取到的值,之后每进来一个货币,就把能取到的值和使用情况的数组更新一下。
要注意减枝,如果一种货币加进去这枚之后没有让能取到的值发生变化,后面和它相同面值的货币一概不检查。
(实际感觉这个算法还是蛮强大的枚举都能把这题水过去。。。如果把上面数的拆分加进去应该能0msAC)
dp代码
#include<iostream>
#include<Cstdio>
#include<string>
using namespace std;
bool used[110001];
int map[110001];
int main()
{
int cash, i, num, lim, j, size, p, temp;
int count, pre; bool flag;
while(cin>>cash>>num)
{
used[0] = true;
for(i = 1; i <= cash; i++)
{
used[i] = false;
}
map[0] = 0;
count = 1;
while(num--)
{
scanf("%d%d", &size, &temp);
flag = true;
while(size--)
{
pre = count;
for(i = 0; i < pre;i++)
if(map[i] + temp <= cash && !used[map[i] + temp])
{
flag = false;
used[map[i] + temp] = true;
map[count++] = map[i] + temp;
}
if(flag)
break;
}
}
j = cash;
while(!used[j])
j--;
cout << j << endl;
}
}
枚举代码
#include<iostream>
#include<Cstdio>
#include<string>
using namespace std;
int dp[2][110001];
int temp[2001];
int ret(int size)
{
int temp = 0, sum = 1;
while(sum <= size)
{
sum *= 2;
temp++;
}
return temp - 1;
}
int main()
{
int cash, i, num, lim, j, size, p, temp, q;
int c1, s1, mem; bool flag;
while(cin>>cash>>num)
{
for(i = 0; i <= cash; i++)
{
dp[0][i] = 0;
}
p = 0;
while(num--)
{
scanf("%d%d", &size, &temp);
if(size != 0 && temp != 0)
{
if(size == 1)
{
for(i = 0; i <= cash; i++)
{
if(i < temp)
{
dp[1-p][i] = dp[p][i];
continue;
}
q = dp[p][i - temp] + temp;
if(q > dp[p][i])
dp[1 - p][i] = q;
else
dp[1-p][i] = dp[p][i];
}
p = 1 - p;
}
else
{
lim = ret(size);
mem = temp;
for(i = 0; i <= lim; i++)
{
if(temp > cash)
break;
if(i == lim)
temp = mem * (size + 1) - temp;
for(j = 0; j <= cash; j++)
{
if(j < temp)
{
dp[1-p][j] = dp[p][j];
continue;
}
q = dp[p][j - temp] + temp;
if(q > dp[p][j])
dp[1 - p][j] = q;
else
dp[1-p][j] = dp[p][j];
}
temp *= 2;
p = 1 - p;
}
}
}
}
cout << dp[p][cash] << endl;
}
}