Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 32571 | Accepted: 11789 |
Description
N=3, n1=10, D1=100, n2=4, D2=50, n3=5, D3=10
means the machine has a supply of 10 bills of @100 each, 4 bills of @50 each, and 5 bills of @10 each.
Call cash the requested amount of cash the machine should deliver and write a program that computes the maximum amount of cash less than or equal to cash that can be effectively (有效地) delivered according to the available bill supply of the machine.
Notes:
@ is the symbol (象征) of the currency delivered by the machine. For instance (实例), @ may stand for dollar, euro, pound etc.
Input
cash N n1 D1 n2 D2 ... nN DN
where 0 <= cash <= 100000 is the amount (数量) of cash requested, 0 <=N <= 10 is the number of bill denominations (面额) and 0 <= nk <= 1000 is the number of available bills for the Dk denomination, 1 <= Dk <= 1000, k=1,N. White spaces can occur freely between the numbers in the input. The input data are correct.
Output
Sample Input
735 3 4 125 6 5 3 350 633 4 500 30 6 100 1 5 0 1 735 0 0 3 10 100 10 50 10 10
Sample Output
735 630 0 0
这个题就是个多重背包,题意就不用多说了。
思路就是,从背包为0的状态,开始枚举装进物品,装进物品后,背包就会达到一个更大的状态,此时更新这个最大状态。
借用别人的话就是:
其中dp[j]记录的是“最接近状态j,且<=j”的状态值,即dp[j] <=j
J越接近dp[j],dp[j]的解越优,最理想是dp[j]=j
需要注意的是,dp[j]为了说明的是状态j可以发生,但不会理会dp[j]怎样发生
例如有3张1元,1张3元
那么我们既可以认为dp[3]=3是通过取3次1元得到,也可以认为dp[3]是通过取1次3元得到的,但无论怎样得到,dp[3]=3都会发生,
再需要注意的是,dp[j]的状态值会累积
再形象举例说明:例如有1张3元,cash=4
那么根据前面的说明,自然有dp[3]=dp[4]=3,就是说状态3、状态4都可以通过取1次3元发生,一旦发生,这个状态值3就会被保有在当前的状态dp[4],这其实是起到一个优化作用,后面详细解释
再接上例,当我们增加一个条件“有3张1元”,那么在已经被保存的前提“dp[4]=3”下,我们只需要通过取1次1元,就能得到比dp[4]=3的更优解dp[4]=4,但此时我们完全不用理会dp[4]=3是怎样来的,我们只需要知道dp[4]=3一定会出现就足够了
然后说说刚才提到的“优化问题”
利用01背包的知识,不难理解 状态方程 dp[j]=dp[ j-c[i] ]+w[i]
至于说方程是什么含义,看过01背包的大概也知道什么意思,没看的同学就别怪我不解释咯O(∩_∩)O
优化是因为状态值被记录了,就是说我们在取得下一个货币i之前,当前的状态为j-c[i]
一旦我们选取货币i,就会得到状态(j-c[i])+c[i],即状态j 。且状态j的状态值dp[j]会加上w[i]。 至于dp[j]原来值是多少,就无需理会,因为dp[j]的值是累积下来的,同样本次加上w[i]后,只要dp[j]值未到最优,它同样会成为下一次的累加值。
这样每次都直接调用前一次已获得的状态值,就可以节省一堆搜索的时间,这是DFS或BFS办不到的,也是动态规划的优点。
所以,我们每次从已经更新的状态出发装新的物品,达到一个更接近容量或者比容量大的值,更新答案即可。当然物品是至少要装一个的。这里dp数组是用来记录这个状态有没有出现过得。#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN=1005;
const int inf=0x3f3f3f;
int n,m;
int w[15];
int num[15];
int dp[100005];
int main()
{
int i,j,k;
while(~scanf("%d%d",&n,&m))
{
for(i=0;i<m;++i)
{
scanf("%d%d",&num[i],&w[i]);
}
if(!n||!m)
{
puts("0");
continue;
}
for(i=0;i<=n;++i)dp[i]=0;
int MAX=0;
dp[0]=1;
for(i=0;i<m;++i)
for(j=MAX;j>=0;--j)if(dp[j])//这里要倒着来,当然也是防止一个物品多次放置
for(k=1;k<=num[i];++k)
{
int x=w[i]*k+j;
if(x>n)break;//x已经大于n,那后面的比它更大
dp[x]=1;
if(x>MAX)MAX=x;//更新状态值
}
printf("%d\n",MAX);
}
return 0;
}