一些背包题

时间:2019-10-22
本文章向大家介绍一些背包题,主要包括一些背包题使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

HDU2546

本题的核心思想基于贪心+01背包,因为当卡内余额小于5时无法购买东西,为了让余额最小,总是会有卡内余额大于等于5块钱时的最大消费+价格最大的菜品这样一个贪心表达式,即开头先把最大值挑出来,然后在01背包的过程结束后减去这个最大值。

这里01背包的作用是确定m-5个单位余额最大可以买到多少价格单位的菜,所以容积上界应该设定为m-5。

//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
//    BigInteger tmp = 0, si = 1;char c;    c = getchar();
//    while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
//    while (isdigit(c))
//    {tmp = tmp * 10 + c - '0';c = getchar();}
//    return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
//    if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
//    return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
//    if (x < 0)
//    {x = -x;putchar('-');}
//    if (x > 9) output(x / 10);
//    putchar(x % 10 + '0');
//    }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
ll arr[23222];
ll dp[1500][1500];
int DETERMINATION()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    ll n;
    while (cin >> n && n)
    {
        ll index = 0, mx = 0;
        for (int i = 1; i <= n; i++)
        {
            cin >> arr[i];
            if (arr[i] > mx)
            {
                mx = arr[i];
                index = i;
            }
        }
        ll m;
        cin >> m;
        if (m < 5)
            cout << m << endl;
        else
        {
            arr[index] = 0;
            reset(dp, 0);
            ll ans = 0;
            for (int i = 1; i <= n; i++)
            {
                for (int j = m - 5; j >=0; j--)
                {
                    //dp[j] = max(dp[j], dp[j - arr[i]] + arr[i]);
                    if (j >= arr[i])
                        dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - arr[i]] + arr[i]);
                    else
                        dp[i][j] = dp[i - 1][j];
                    ans = max(dp[i][j], ans);
                }
            }
            //cout << ans << endl;

            //cout << ans << " " << mx << endl;
            cout << m - ans - mx << endl;
        }
    }
    return 0;
}

UVA624

本题是一个01背包+记录路径的题目,因为需要记录路径,就需要额外添加一个记录路径用的数组。在最后可以通过这个数据进行回溯求解放入的物品。

二维DP数组与滚动数组不同的一点是,无法放入物品时也要继承dp[i-1][j]的结果,否则会出现断档的情况。

//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
//    BigInteger tmp = 0, si = 1;char c;    c = getchar();
//    while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
//    while (isdigit(c))
//    {tmp = tmp * 10 + c - '0';c = getchar();}
//    return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
//    if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
//    return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
//    if (x < 0)
//    {x = -x;putchar('-');}
//    if (x > 9) output(x / 10);
//    putchar(x % 10 + '0');
//    }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
ll arr[23222],dp[27][100005];
ll path[27][100005];
int DETERMINATION()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0), std::cout.tie(0);
	ll volume, num;
	while (cin >> volume >> num)
	{
		for (int i = 1; i <= num+5; i++)
		{
			for (int j = 0; j <= volume+5; j++)
			{
				dp[i][j] = 0;
				path[i][j] = 0;
			}
		}
		for (int i = 1; i <= num; i++)
			cin >> arr[i];
		dp[0][0] = 0;
		for (int i = 1; i <= num; i++)
		{
			for (int j = volume; j >= 0; j--)
			{
				//if (i == 3 && j == 5)
				//dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - arr[i]] + arr[i]);
				if (arr[i] <= j)
				{
					if (dp[i - 1][j - arr[i]] + arr[i] >= dp[i - 1][j])
					{
						dp[i][j] = dp[i - 1][j - arr[i]] + arr[i];
						path[i][j] = i;
					}
					else
					{
						dp[i][j] = dp[i - 1][j];
						path[i][j] = path[i - 1][j];
					}
				}
				else
				{
					dp[i][j] = dp[i - 1][j];
					path[i][j] = path[i - 1][j];
				}
			}
		}
		ll ans = 0, indexi = 0, indexj = 0;
		for (int i = 1; i <= num; i++)
		{
			for (int j = 0; j <= volume; j++)
			{
				if (dp[i][j]>ans)
				{
					ans = dp[i][j];
					//cout << dp[i][j] << endl;
					indexi = i;
					indexj = j;
				}
			}
		}
		stack<ll>ans1;
		//cout << indexi << " " << indexj << endl;
		//cout << path[2][1] << endl;
		for (int i = indexi, j = indexj; path[i][j] != 0;j-=arr[path[i][j]],i-=1)
			ans1.push(arr[path[i][j]]);
		while (ans1.empty() == false)
		{
			cout << ans1.top() << " ";
			ans1.pop();
		}
		cout << "sum:" << ans<< endl;
	}
	return 0;
}

HDU2955

The aspiring Roy the Robber has seen a lot of American movies, and knows that the bad guys usually gets caught in the end, often because they become too greedy. He has decided to work in the lucrative business of bank robbery only for a short while, before retiring to a comfortable job at a university.

渴望成功的RR看了不少美国电影,了解到坏人经常在最后落入法网的原因是他们过于贪婪。他从决定从事抢劫到现在只有一小段时间,这是在他从大学的某个轻松的职位上退休后的想法。

For a few months now, Roy has been assessing the security of various banks and the amount of cash they hold. He wants to make a calculated risk, and grab as much money as possible.

近期几个月,R已经入侵了许多银行的安全系统得到了这些银行拥有的资金数目,他想计算出风险系数,并且还要得到尽可能多的钱。

His mother, Ola, has decided upon a tolerable probability of getting caught. She feels that he is safe enough if the banks he robs together give a probability less than this.

他母亲,O,已经定好了一个可容忍的被抓概率。如果抢一些银行的被抓总概率小于这个数,就认为这个行动是安全的。

首先这个题的与众不同之处是在于独立事件的概率的计算是乘法计算,与一般的加法背包不同,其次是这里两个数据中代表“容积”的变量是实数变量,实数是不能作下标的,如果用map也不知道循环的步长是多少,所以需要一些变动。

背包问题只能求解最大值(最小值就是什么也不装),按一般想法这里是求出在可容忍被抓概率下的最大钱数,因为上述原因这个方案是不可行的,所以换个思路,求解一定范围钱数下的最大不被抓概率。被抓和不被抓是对立事件,所以直接用1-p就能求出不被抓概率,然后利用背包求出0~所有银行钱数总和的最大不被抓概率,最后从大到小判断一下它们对应的最大不被抓概率与安全概率的关系,输出答案即可。

//#include<pch.h>
#include <iostream>
#include <cstdio>
#include <bits/stdc++.h>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <iomanip>
#include <cstring>
#include <cmath>
//#define endl '\n'
#define DETERMINATION main
#define For(a,b,c,d) for(int a=b;a<=c;a+=d)
#pragma GCC optimize(2)
#pragma warning(disable:4996)
#define lldin(a) scanf("%lld", &a)
#define println(a) printf("%lld\n", a)
#define print(a) printf("%lld ", a)
#define reset(a, b) memset(a, b, sizeof(a))
#define debug std::cout<<"procedures above are available"<<"\n";
#define BigInteger __int128
using namespace std;
const long long INF = 2147483647;
const double PI = acos(-1);
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int mod = 1e9 + 7;
//template<typename T>
//inline BigInteger nextBigInteger()
//{
//    BigInteger tmp = 0, si = 1;char c;    c = getchar();
//    while (!isdigit(c))
//{if (c == '-')si = -1;c = getchar();}
//    while (isdigit(c))
//    {tmp = tmp * 10 + c - '0';c = getchar();}
//    return si * tmp;
//}
//std::ostream& operator<<(std::ostream& os, __int128 T)
//{
//    if (T<0) os<<"-";if (T>=10 ) os<<T/10;if (T<=-10) os<<(-(T/10));
//    return os<<( (int) (T%10) >0 ? (int) (T%10) : -(int) (T%10) ) ;
//}
//void output(BigInteger x)
//{
//    if (x < 0)
//    {x = -x;putchar('-');}
//    if (x > 9) output(x / 10);
//    putchar(x % 10 + '0');
//    }
/**Operation Overlord 1944.6.6 Daybreak**/
/**Last Remote**/
ll fund[15000];
double possi[15000];
double dp[15000];
int DETERMINATION()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0); 
    ll t;
    cin >> t;
    for (int y = 1; y <= t; y++)
    {
        double p;
        ll n;
        cin >> p >> n;
        p = 1 - p;
        ll sum = 0;
        for (int i = 1; i <= n; i++)
        {
            cin >> fund[i] >> possi[i];
            sum += fund[i];
        }
        reset(dp, 0);
        dp[0] = 1;
        for (int i = 1; i <= n; i++)
        {
            for (int j = sum; j >= fund[i]; j--)
                dp[j] = max(dp[j], dp[j - fund[i]]*(1 - possi[i]));
        }
        ll pt = sum;
        while (dp[pt] < p)
            pt--;
        cout << pt << endl;
    }
    return 0;
}

POJ2184

本题的难点在于负数处理上,因为最大和可能是一个负数与一个大正数的和,而两个数据维度都可能是那个负数,所以一般的模板不能够处理这个题目,使用map很可能会超时。

可以看到本题最大数据总和为[-100000,100000],这也就是说可以把区间总体移到[0,200000]间,这可以通过把零点设定为100000实现,小于1e5的就是负数,大于1e5的就是正数。

这样就可以利用数组存储了,但是还有一个问题:滚动数组优化后容积一般都是从大到小循环,但是这里出现了一个减负数的问题,这样就变成了优先访问大容积,所以对于这个问题可以通过从小到大循环来解决(可能直接把负数处理变成正数亦可)。

必须注意的是负数的边界上限为2e5+it[i],或者在初始化的时候多初始化一点,否则会出现j-it[i]>2e5,访问到未初始化部分的情况

ll dp[(ll)2e5 + 9];
ll it[5000], fn[5000];
int DETERMINATION()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(0), std::cout.tie(0);
	ll n;
	cin >> n;
	for (int i = 0; i <= 2e5+155; i++)
		dp[i] = -INF;
	//cout << dp[2] << endl;
	for (int i = 1; i <= n; i++)
		cin >> it[i] >> fn[i];
	dp[(ll)1e5] = 0;
	ll ans = 0;
	for (int i = 1; i <= n; i++)
	{
		if (fn[i] > 0 || it[i] > 0)
		{
			if (it[i] > 0)
			{
				for (int j = (ll)2e5; j >= it[i]; j--)
				{
					if (dp[j - it[i]] !=-INF)
						dp[j] = max(dp[j], dp[j - it[i]] + fn[i]);
					if (dp[j] >=0 && j >= 1e5)
					{
						ans = max(ans, dp[j] + j - (ll)1e5);
						//cout << dp[j] << " " << j << endl;
					}
				}
			}
			else
			{
				for (int j = it[i]; j <= (ll)2e5+it[i]; j++)
				{
					if (dp[j - it[i]] != -INF)
						dp[j] = max(dp[j], dp[j - it[i]] + fn[i]);
					if (dp[j] >=0&&j>=1e5)
					{
						ans = max(ans, dp[j] + j - (ll)1e5);
						//cout << dp[j] << " " << j << endl;
					}
				}
			}
		}
		
	}
	cout << ans << endl;
	return 0;
}

原文地址:https://www.cnblogs.com/HardBass/p/11720096.html