http://poj.org/problem?id=1276 Description 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: Input cash N n1 D1 n2 D2 ... nN DN Sample Input |
题意:给出一个价值cash(金额,相当于背包容量),然后给出n,代表n种钞票(相当于n个物品),接着n对代表个数与面值(相当于物品i的个数与体积),要求最接近cash,但不超过cash的价值(尽量将背包装满,但不能超过背包容量)
思路:这题一看就知道是多重背包,直接挨个考虑不太行,得加点剪枝思维,倒是不难理解,
代码:
解法1:这个解法比较有趣,利用了二进制,可以构成任意数的特点,。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int dp[100005], V;
void Alone(int use)
{
int v;
for(v=V;v>=use;v--)
dp[v] = max(dp[v], dp[v-use]+use);
}
void Every(int use)
{
int v;
for(v=use;v<=V;v++)
dp[v] = max(dp[v], dp[v-use]+use);
}
void Mulitp(int n, int use)
{
int k;
if(use>V)
return;
if(use*n>=V)
Every(use);///当前物品最大体积总体积超过背包,背包容量为优先限制条件。相当于可以往背包内放任意多个的做法;
else
{
k = 1;
while(k<n)///依次往背包内放1,2,4,8。。。个物品,跟据2进制特点,可以组合成任意值,并且个数永远不会超过物品的最大数量。所以可以依次选择放进背包。
{
Alone(k*use);
n -= k;
k *= 2;
}
Alone(n*use);
}
}
int main(void)
{
int i, n, s[15], val[15];
while(scanf("%d", &V)!=EOF)
{
memset(dp, 0, sizeof(dp));
scanf("%d", &n);
for(i=1;i<=n;i++)
scanf("%d%d", &s[i], &val[i]);
for(i=1;i<=n;i++)
Mulitp(s[i], val[i]);
printf("%d\n", dp[V]);
}
}
解法2:
比较常规,也是不错的解法
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct node
{
int n,v;
} a[20];
int dp[100010];
int main()
{
int sum,n,i,j,k;
while(~scanf("%d%d",&sum,&n))
{
for(i = 1; i<=n; i++)
scanf("%d%d",&a[i].n,&a[i].v);
if(!sum)
{
printf("0\n");
continue;
}
if(!n)
{
printf("0\n");
continue;
}
memset(dp,0,sizeof(dp));
dp[0] = 1;
int MAX = 0,tem;
for(i = 1; i<=n; i++)
{
for(j = MAX;j>=0;j--)///已放入的背包的最大容量,包含所有dp值为1的最小区间
{
if(dp[j])///可以往背包内放物品
{
for(k = 1;k<=a[i].n;k++)///zaij的基础上,按个数考虑往背包内放。。
{
tem = j+k*a[i].v;
if(tem>sum)
break;
dp[tem] = 1;
if(tem>MAX)
MAX = tem;
}
}
}
}
printf("%d\n",MAX);
}
return 0;
}