一道用来防AK的题,但是被我们给弄出来了,还是挺可以的。
3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0Sample Output
8 4
这道题就是要我们求在一定范围内,能够用手头上的一定数量的钱构成几种类型的总和,譬如一个一块和一个两块,在三块钱范围内能组成{1}、{1+2}、{2}的组合。
思路:这道题如果用普通的多重背包,例如这样(就会超时):
for(int i=0; i
{
for(int j=1; j<=coin[i].b; j++)
{
for(int k=m; k>=coin[i].a*j; k--)
{
if(dp[k-coin[i].a] && !dp[k])
{
dp[k]=1;
sum++;
}
}
}
}
既然都说过了会超时,当然也会讲不超时的算法和思路,
上面我们用了3个for循环O(n^3)的算法当然的T,我们发现第三个for中的
int k=m; k>=coin[i].a*j; k--
会被一遍遍的遍历,那么我们换种方式,假如知道此时取了几次岂不是更好,那么用一个s数组来存储到达这一个值我们所用去的步数就可以了。
利用s[i]与dp[i]同步的方法,记录到达这个值时候的步数,我们仅需要在循环中假如条件:
s[k-coin[i].a]<coin[i].b
即可,但为什么取小于符是因为原式应当为s[k]<=coin[i].b,但由于s[k]为接下来的处理量,所以我们才对它的前一步进行处理。
完整代码:
#include
#include
#include
using namespace std;
int n,m;
struct node
{
int a,b; //价值,数量
}coin[105];
int dp[100005];
int s[100005];
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)break;
memset(coin, 0, sizeof(coin));
memset(dp, 0, sizeof(dp));
dp[0]=1;
int sum=0;
for(int i=0; im)
{
coin[j].b=m/coin[j].a;
}
}
for(int i=0; i