背包
bone collector
Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?
input: The first line contain a integer T , the number of cases. Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.
**output: **One integer per line representing the maximum of the total value (this number will be less than 2[sup]31[/sup]).
分析
这是一个01背包问题,其所要达到的目标是将搜集的骨头的价值最大化,但是限制条件是骨头有一定的体积,而最大体积是有限制的。
#include <iostream>
#include <math.h>
#include <algorithm>
using namespace std;
int main()
{
int T, N, V;
int value[1001]/*代表着骨头的价值*/, volume[1001]/*代表着骨头的体积*/;
int bag[1001];
cin >> T;
while(T--)
{
cin >> N >> V;
//读入数据
for(int i = 0; i < N; i++) cin >> value[i];
for(int i = 0; i < N; i++) cin >> volume[i];
//对背包进行初始化
for(int i = 0; i <= V; i++)
{
bag[i] = 0;
}
for(int i = 0; i < N; i++)//选择第i个骨头要不要拿,i的范围也自然从第0个到第N-1个
{
//因为是01背包,所以从限制的体积开始向前走,走到当前选中的骨头的体积为止
for(int j = V; j >= volume[i]; j--)
{
//背包的状态转移方程
bag[j] = max(bag[j], bag[j-volume[i]] + value[i]);
}
}
cout << bag[V] << endl;
}
return 0;
}
Piggy-Bank
Problem Description
Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversibly Bound Money (IBM). The idea behind is simple. Whenever some ACM member has any small money, he takes all the coins and throws them into a piggy-bank. You know that this process is irreversible, the coins cannot be removed without breaking the pig. After a sufficiently long time, there should be enough cash in the piggy-bank to pay everything that needs to be paid.
But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!
Input
The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing two integers E and F. They indicate the weight of an empty pig and of the pig filled with coins. Both weights are given in grams. No pig will weigh more than 10 kg, that means 1 <= E <= F <= 10000. On the second line of each test case, there is an integer number N (1 <= N <= 500) that gives the number of various coins used in the given currency. Following this are exactly N lines, each specifying one coin type. These lines contain two integers each, Pand W (1 <= P <= 50000, 1 <= W <=10000). P is the value of the coin in monetary units, W is it’s weight in grams.
Output
Print exactly one line of output for each test case. The line must contain the sentence “The minimum amount of money in the piggy-bank is X.” where X is the minimum amount of money that can be achieved using coins with the given total weight. If the weight cannot be reached exactly, print a line “This is impossible.”.
分析
-
题目中给出的限制条件是存钱罐所含的钱的质量,而钱本身是有一定的质量,目标是达到最小的价值;
-
每一种钱都可能不止一种,理论上可以有无限个,所以是完全背包问题;
-
隐含条件是最后的所计算出的前都必须与存钱罐中一致,所以初始化的时候要当心
#include <iostream>
using namespace std;
int main()
{
int T;
cin >> T;
while(T--)
{
int w1, w2, w;//w代表着钱的总重
int coin_kind_num;//钱币的种类数
int value[10001], weight[10001];
int bag[10001];
cin >> w1 >> w2;
w = w2 - w1;
cin >> coin_kind_num;
for(int i = 0; i < coin_kind_num; i++) cin >> value[i] >> weight[i];
//由于题中要求是要将小猪的重量与选中的钱币总重相同,所以初始化为无穷大
for(int i = 0; i <= w; i++) bag[i] = 0x3f3f3f;
bag[0] = 0;//但是第0个需要初始化为0
//选第i种钱币
for(int i = 0; i < coin_kind_num; i++)
{
//完全背包问题,所以需要从当前的限制值走到最大限制值
for(int j = weight[i]; j <= w; j++)
{
//由于是算最小价值,所以需要用min,而非max
bag[j] = min(bag[j], bag[j - weight[i]] + value[i]);
}
}
if(bag[w] == 0x3f3f3f)
{
cout << "This is impossible." << endl;
}
else
printf("The minimum amount of money in the piggy-bank is %d.\n", bag[w]);
}
return 0;
}
悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
Problem Description
为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买。请问:你用有限的资金最多能采购多少公斤粮食呢?
Input
输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数
Output
对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。
分析
- 题中给出了每种米的袋数,说明这是01背包和完全背包的结合体。可以用二进制的思想来将这个问题转换为01背包问题,代码如下:
int count;
void read_in()
{
int p, w, num;
cin >> p >> w >> num;//读入价格,重量,每种米的袋数
int k = 2, i;
//将选中一袋米的情况记录下来
price[count] = p;
weight[count] = w;
count++;//每次记录count必须自加1
//i代表着当前还剩多少袋,由于之前已经读入1袋,所以从i-1开始
for(i = num-1; i-k >= 0/*表示这次选中了k袋之后,这次能不能够满足拿出k袋的需求*/; )
{
//读入相应的选中的k袋对应的价格和重量
price[count] = k * p;
weight[count] = k * w;
count++;
//i变为拿走k袋后剩余的袋数
i = i - k;
//k翻倍
k = 2 * k;
}
//如果最后剩余的袋数不为0,且不能满足二进制的要求
if(i != 0)
{
price[count] = i * p;
weight[count] = i * w;
count++;
}
}
-
限制条件是资金,但是资金可以不用花光,所以初始化时仅需全设置为0;目标是大米的重量最大
#include <iostream> using namespace std; int price[1001], weight[1001]; int count; void read_in() { int p, w, num; cin >> p >> w >> num;//读入价格,重量,每种米的袋数 int k = 2, i; //将选中一袋米的情况记录下来 price[count] = p; weight[count] = w; count++;//每次记录count必须自加1 //i代表着当前还剩多少袋,由于之前已经读入1袋,所以从i-1开始 for(i = num-1; i-k >= 0/*表示这次选中了k袋之后,这次能不能够满足拿出k袋的需求*/; ) { //读入相应的选中的k袋对应的价格和重量 price[count] = k * p; weight[count] = k * w; count++; //i变为拿走k袋后剩余的袋数 i = i - k; //k翻倍 k = 2 * k; } //如果最后剩余的袋数不为0,且不能满足二进制的要求 if(i != 0) { price[count] = i * p; weight[count] = i * w; count++; } } int main() { int T; cin >> T; while(T--) { int money, kinds; cin >> money >> kinds; count = 0;//初始化count for(int i = 0; i < kinds; i++)//读入k种米 { read_in(); } int bag[101]; //因为已经转化为01背包,初始化为0 for(int i = 0; i <= money; i++) bag[i] = 0; for(int i = 0; i < count/*这时选的物品数并不是kinds,而是转化后的count!!!!*/; i++) { for(int j = money; j >= price[i]; j--)//01背包,从后往前 { bag[j] = max(bag[j], bag[j - price[i]] + weight[i]); } } cout << bag[money] << endl; } return 0; }
FATE
Problem Description
最近 xhd 正在玩一款叫做FATE的游戏,为了得到极品装备,xhd 在不停的杀怪做任务。久而久之 xhd 开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd 升掉最后一级还需n的经验值,xhd 还留有m的忍耐度,每杀一个怪 xhd 会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd 就不会玩这游戏。xhd 还说了他最多只杀s只怪。请问他能升掉这最后一级吗?
Input
输入数据有多组,对于每组数据第一行输入n,m,k,s(0 < n,m,k,s < 100)四个正整数。分别表示还需的经验值,保留的忍耐度,怪的种数和最多的杀怪数。接下来输入k行数据。每行数据输入两个正整数a,b(0 < a,b < 20);分别表示杀掉一只这种怪xhd会得到的经验值和会减掉的忍耐度。(每种怪都有无数个)
Output
输出升完这级还能保留的最大忍耐度,如果无法升完这级输出-1。
分析
-
二维背包,限制条件分别为忍耐度,最多杀怪数,目标是打怪时花费的最小忍耐度,并不是打怪获得的最大经验值,但也要判断会不会达到要求的经验值
#include <iostream> using namespace std; int v[101], w[101]; int bag[101][101]; int main() { int jin, ren, kinds, num_max; while(cin >> jin >> ren >> kinds >> num_max) { for(int i = 0; i < kinds; i++) cin >> v[i] >> w[i]; for(int i = 0; i <= ren; i++) { for(int j = 0; j <= num_max; j++) { bag[i][j] = 0;//二维背包,两个限制值 } } int mini = 101;//最小花费忍耐度 for(int i = 0; i < kinds; i++) { //两个for循环,外层控制忍耐度,内层控制杀怪数 for(int j = w[i]; j <= ren; j++) { for(int k = 1; k <= num_max; k++) { //状态转移 bag[j][k] = max(bag[j][k], bag[j-w[i]][k-1] + v[i]); //更新最小花费忍耐度 if(bag[j][k] >= jin) { mini = min(mini, j); } } } } //要不要输出最小忍耐度的条件 if(bag[ren][num_max] >= jin) { cout << ren - mini << endl; } else { cout << -1 << endl; } } return 0; }