poj1276(dp) Cash Machine

    xiaoxiao2026-08-04  2

    Cash Machine Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 32571 Accepted: 11789

    Description

    A Bank plans to  install (安装) a machine for cash  withdrawal (撤退). The machine is able to deliver  appropriate (适当的) @ bills for a requested cash  amount (数量). The machine uses exactly N distinct (明显的) bill  denominations (面额), say Dk, k=1,N, and for each denomination Dk the machine has a supply of nk bills. For example, 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

    The program  input (投入) is from standard input. Each data set in the input stands for a particular  transaction (交易) and has the format: 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

    For each set of data the program prints the result to the standard  output (输出) on a separate line as shown in the examples below.

    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]怎样发生

    例如有31元,13

    那么我们既可以认为dp[3]=3是通过取31元得到,也可以认为dp[3]是通过取13元得到的,但无论怎样得到,dp[3]=3都会发生,

    再需要注意的是,dp[j]的状态值会累积

    再形象举例说明:例如有13,cash=4

    那么根据前面的说明,自然有dp[3]=dp[4]=3,就是说状态3、状态4都可以通过取13元发生,一旦发生,这个状态值3就会被保有在当前的状态dp[4],这其实是起到一个优化作用,后面详细解释

    再接上例,当我们增加一个条件“有31元”,那么在已经被保存的前提“dp[4]=3”下,我们只需要通过取11元,就能得到比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的状态值dp[j]会加上w[i] 至于dp[j]原来值是多少,就无需理会,因为dp[j]的值是累积下来的,同样本次加上w[i]后,只要dp[j]值未到最优,它同样会成为下一次的累加值。

    这样每次都直接调用前一次已获得的状态值,就可以节省一堆搜索的时间,这是DFSBFS办不到的,也是动态规划的优点。

    所以,我们每次从已经更新的状态出发装新的物品,达到一个更接近容量或者比容量大的值,更新答案即可。当然物品是至少要装一个的。这里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; }

    转载请注明原文地址: https://ju.6miu.com/read-1310840.html
    最新回复(0)