Problem Description
The cows are going to space! They plan to achieve orbit by building a sort of space elevator: a giant tower of blocks. They have K (1 <= K K K <= 400) different types of blocks with which to build the tower. Each block of type i i i has height h i h_{i} hi (1 <= h i h_{i} hi <= 100) and is available in quantity c i c_{i} ci(1 <= c i c_{i} ci<= 10). Due to possible damage caused by cosmic rays, no part of a block of type i can exceed a maximum altitude a i a_{i } ai(1 <= a i a_{i} ai <= 40000).
Help the cows build the tallest space elevator possible by stacking blocks on top of each other according to the rules.
Input
* Line 1: A single integer, K K K
* Lines 2… K + 1 K+1 K+1: Each line contains three space-separated integers: h i h_{i} hi, a i a_{i} ai, and c i c_{i} ci. Line i + 1 i+1 i+1 describes block type i i i.
Output
* Line 1: A single integer H H H, the maximum height of a tower that can be built
Sample Input
3
7 40 3
5 23 8
2 52 6
Sample Output
48
Hint
OUTPUT DETAILS:
From the bottom: 3 blocks of type 2, below 3 of type 1, below 6 of type 3. Stacking 4 blocks of type 2 and 3 of type 1 is not legal, since the top of the last type 1 block would exceed height 40.
题意:有n种石头,每个石头的高度
h
i
h_{i}
hi,允许用的最高高度为
a
i
a_{i}
ai,数量为
c
i
c_{i}
ci,求能组合的最高高度。
思路:先给这些石头排个序,最高高度小的在前面,这样才能使得高度最大,然后再按多重背包来做,这样就可以了。
下一题简单介绍多重背包:
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[40010];//可以认为是标记能否达到最大高度
struct node
{
int hi,ai,ci;
bool operator <(const node &a)const
{
return ai<a.ai;
}
} a[500];
bool cmp(node a,node b)
{
return a.ai<b.ai;
}
int main()
{
int n;
while(cin>>n)
{
memset(dp,0,sizeof(dp));
for(int i=0; i<n; i++)
cin>>a[i].hi>>a[i].ai>>a[i].ci;//hi 高度 ai限度 ci 数量
sort(a,a+n);
dp[0]=1;
for(int i=0; i<n; i++)
for(int j=1; j<=a[i].ci; j++)//hi 高度 ai限度 ci 数量
for(int k=a[i].ai; k>=a[i].hi; k--)
dp[k]|=dp[k-a[i].hi];// 遍历只要有一个能达到最大限度那么就一定能达到
for(int i=a[n-1].ai; i>=0; i--)// i 表示能达到的高度
if(dp[i]==1)
{
cout<<i<<endl;
break;
}
}
return 0;
}
实践才是检验真理的唯一标准;
Cash Machine POJ - 1276
Problem 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 N N distinct bill denominations, say D k D_{k} Dk, k = 1 , N k=1,N k=1,N, and for each denomination D k D_{k} Dk the machine has a supply of n k n_{k} nk bills. For example,
N = 3 , n 1 = 10 , D 1 = 100 , n 2 = 4 , D 2 = 50 , n 3 = 5 , D 3 = 10 N=3, n_{1}=10, D_{1}=100, n_{2}=4, D_{2}=50, n_{3}=5, D_{3}=10 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
⋅
n
1
⋅
D
1
⋅
n
2
⋅
D
2
⋅
⋅
⋅
⋅
⋅
n
N
⋅
D
N
N \cdot n_{1} \cdot D_{1} \cdot n_{2} \cdot D_{2} \cdot \cdot\cdot\cdot\cdot n_{N} \cdot D_{N}
N⋅n1⋅D1⋅n2⋅D2⋅⋅⋅⋅⋅nN⋅DN
where
0
<
=
c
a
s
h
<
=
100000
0 <= cash <= 100000
0<=cash<=100000 is the amount of cash requested,
0
<
=
N
<
=
10
0 <=N <= 10
0<=N<=10 is the number of bill denominations and
0
<
=
n
k
<
=
1000
0 <= n_{k} <= 1000
0<=nk<=1000 is the number of available bills for the
D
k
D_{k}
Dk denomination,
1
<
=
1 <=
1<=
D
k
D_{k}
Dk
<
=
1000
<= 1000
<=1000,
k
=
1
,
N
k=1,N
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.
题目描述:给定一个目标cash数目m,现在你拥有n种不同的cash分别编号1~n,不同种类的cash有着不同的面值v、数目a,现在从你所拥有的cash中任意抽取一些,使得总的cash数最接近m并且不大于m。
本题运用到了多重背包
简单介绍一下多重背包
多重背包问题的思路跟完全背包的思路非常类似,只是k的取值是有限制的,因为每件物品的数量是有限制的,状态转移方程为:
dp[i][v] = max{dp[i - 1][v - k * c[i]] + w[i] | 0 <=k <= n[i]}
其中c[i]是物品的数量,和完全背包的不同支出在于完全背包可以取无数件,而多重背包给定了最多能取的数量。这样也是三个循环,分别是背包容量,物品个数和物品种类。这样如果数量比较多的情况,很明显这个做法也会超时,所以我们也要想更优化的方法去完善。
转化为01背包问题
把第i种物品换成n[i]件01背包中的物品。考虑二进制的思想,考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0…n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现
方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为
1
,
2
,
4
,
⋅
⋅
⋅
⋅
⋅
⋅
,
2
k
−
1
,
n
[
i
]
−
2
k
+
1
1,2,4,\cdot\cdot\cdot\cdot\cdot\cdot,2^{k-1},n[i]-2k+1
1,2,4,⋅⋅⋅⋅⋅⋅,2k−1,n[i]−2k+1,且k是满足
n
[
i
]
−
2
k
+
1
>
0
n[i]-2^k+1>0
n[i]−2k+1>0的最大整数。例如,如果
n
[
i
]
为
13
n[i]为13
n[i]为13,就将这种物品分成系数分别为
1
,
2
,
4
,
6
1,2,4,6
1,2,4,6的四件物品。
分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于
0
⋅
⋅
⋅
⋅
n
[
i
]
0\cdot\cdot\cdot\cdot n[i]
0⋅⋅⋅⋅n[i]间的每一个整数,均可以用若干个系数的和表示,证明我也没看过这里就不贴上了,主要还是需要去理解代码,代码在下面给出:
本题没有用到二进制思想,因给出出数据不大但是此处用到了按位或(“|”);
按位或
|
表示按位或按位或运算 按位或运算符“|”是双目运算符。
其功能是参与运算的两数各对应的二进位(也就是最后一位)相或。
只要对应的二个二进位有一个为1时,结果位就为1。
128: (0000 1000 0000)
127: (0000 0111 1111) (高位用0补齐)
按位或的结果就是(0000 1111 1111)=255
本题代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int dk[1010];
int dp[100100];
int main()
{
int cash,n;
while(cin>>cash>>n)
{
int k=0,b;
for(int i=0; i<n; i++)
{
cin>>b>>dk[k];
int a=1,c=dk[k];
while(a<b) //这一步是把b张转化成2进制的数
{ //因此dk类钞票可以1张为一组2张为一组4张为一组8张为一组.....
dk[k++]=c*a;//相当把所有可能进行展开之后运用01背包可以解决题目;
b-=a;
a=a*2;
}
if(b)
dk[k++]=b*c;
}
memset(dp,0,sizeof(dp));
for(int i=0; i<k; i++) //o-1背包
for(int j=cash; j>=dk[i]; j--)
dp[j]=max(dp[j-dk[i]]+dk[i],dp[j]);
cout<<dp[cash]<<endl;
}
return 0;
}