poj1276 Cash Machine(多重背包)

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
Hint

The first data set
designates a transaction where the amount of cash requested is @735. The
machine contains 3 bill denominations: 4 bills of @125, 6 bills of @5, and 3
bills of @350. The machine can deliver the exact amount of requested cash.

In the second case the bill supply of the machine does not fit the exact amount
of cash requested. The maximum cash that can be delivered is @630. Notice that
there can be several possibilities to combine the bills in the machine for
matching the delivered cash.

In the third case the machine is empty and no cash is delivered. In the fourth
case the amount of cash requested is @0 and, therefore, the machine delivers no
cash.

//会T的方法(未优化),了解一下思路
#include<bits/stdc++.h>
using namespace std;
const int M = 100005;
int cash, N, dp[M], su[12], v[12];
int main() {
        while(~scanf("%d%d", &cash,&N)) {
        	memset (dp, 0, sizeof(dp));
                for (int i = 0; i < N; i++)
                scanf("%d%d", &su[i],&v[i]);//su是货币的数量,v是货币的价值
                for (int i = 0; i < N; i++) {
            		//拿每一种货币时
                     	//每次循环都用dp记录j上能放的最大价值
                        for (int j = 1; j <=cash; j++) {
                             //for (int j = cash; j > 0; j--) 效果相同
                             //用dp[j]记录在该拿货币时拿了多少钱
                             for (int k =0; k <= su[i]; k++) {//k是该货币拿的个数

                                    if(k*v[i] <= j)
                       	 		dp[j] = max(dp[j],dp[j-k*v[i]] + k*v[i]); //dp记录拿了多少钱
                                    else break;
                                  }
                         }
                 }
        printf("%d\n", dp[cash]);
        }
}

因为 0 <= cash <= 100000, 0 <=N <= 10,0 <= nk <= 1000,可以对nk进行处理,减少循环次数。
(1)优化

//优化一
#include <bits/stdc++.h>
using namespace std;
const int M = 100005;
int cash, n, dp[M], v, su;
int main() {
        while (scanf("%d%d",&cash, &n) != EOF) {
                 memset(dp, 0, sizeof(dp));
                 for (int i = 1; i <= n; i++) {
                         scanf("%d%d",&su, &v);
                         for (int k = 1; k <=su; su -= k, k <<= 1) {
                                  for (int j =cash; j >= v * k; j--) {//j储存拿了多少钱,每件物品最多拿拿了的k件*价值
                                        dp[j]=max(dp[j], dp[j - v*k] + v*k);
                                  }
                         }
                         if (su > 0) {
                                  for (int j =cash; j >= su*v; j--) {
                                          dp[j]= max(dp[j], dp[j - su*v] + su*v);
                                  }
                         }
                 }
                 printf("%d\n",dp[cash]);
        }
        return 0;
}

(2)二进制优化
也是对nk进行处理。
已知: 1、2、4、8 、16 、 32……2^n 可以组成从1到2^(n+1)-1中的任何数。

将num件,价值v,花费cost的物品(v,w)拆分为(v,w),(v×2^1, w× 2^1), (v×2^2, w×2^2),⋯,(x,y) 其中(x,y)是无法被拆分剩下来的余项),如此便可转化为01背包问题。

#include <bits/stdc++.h>
using namespace std;
const int M = 100005;
int cash, n, dp[3][M], v, su, a[M], L;//a[M]记录分解的物品;
int main() {
    while (scanf("%d%d",&cash, &n) != EOF) {
        memset(dp, 0, sizeof(dp));
        memset(a, 0, sizeof(a));
        int m = 0;//记录被分解后每个物品的价值,转化为01背包问题
        for (int i = 1; i <= n; i++) { //二进制拆分
            scanf("%d%d",&su, &v);
            int ss = 1;//ss = 1, 2 ,4 ,8...
            while (su >= ss)  {
                a[m++] = ss * v;
                su -= ss;
                ss <<= 1;
            }
            if (su > 0){ //二进制优化后剩余的原来物品对应的数量
                a[m++] = su * v;
            }
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j <= cash; j++) {//j从0开始,因为m从0开始
                    L = 1 & i;//if(L==偶数) (L & 1) = 0;else (L & 1) = 1
                    dp[L][j] = dp[L ^ 1][j];//if(L == 1)(L ^ 1) = 0;else (L ^ 1) = 1;
                    if (j >= a[i])
                        dp[L][j] = max(dp[L ^ 1][j - a[i]] + a[i], dp[L][j]);
            }
        }
        printf("%d\n",dp[L][cash]);
    }
    return 0;
}
发布了25 篇原创文章 · 获赞 24 · 访问量 594

猜你喜欢

转载自blog.csdn.net/rainbowsea_1/article/details/104477021