概述
做ccfcsp2022-9-2 何以包邮这一题时,发现了零一背包还能反着用,故记录一下.
01背包正常用法
借用蓝桥云课题目-小明的背包
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,v;
cin>>n>>v;
vector<pair<int,int>> a(n);
for(int i=0;i<n;i++){
cin>>a[i].first>>a[i].second;
}
vector<int> dp(v+1,0);
for(int i=0;i<n;i++){
for(int j=v;j>=0;j--){
if(j>a[i].first)
dp[j]=max(dp[j],dp[j-a[i].first]+a[i].second);
}
}
cout<<dp[v];
}
ccfcsp-何以包邮?
思路
01背包一般挑选总重量不超过n的物品下使得总价值最大,但是这道题目要求挑选总花费>x的时候花费最小的情况.暴力的想法是枚举所有书的组合,找到价格大于x的最小的情况.但是时间肯定会超.我们考虑dp[i][j]是前i个物品中,能否挑选其中任意个物品,使得组合出的价值为j.当前i-1个物品能组合出价值为j-a[i]的组合时,加上当前(第i个)物品,就能组合出,价值为j的组合.当遍历完所有物品后,就是所有物品能组合出的所有价值情况.在本题目中价值就是花费,只要找到大于x的最小花费就可以了.
AC代码
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
ll mod = 1e9 + 7;
int main()
{
int n, x;
cin >> n >> x;
vector<int> a(n);
int allv = 0;
for (int i = 0; i < n; i++)
{
cin >> a[i];
allv += a[i];
}
//采用滚动数组,和普通背包的滚动数组一样
vector<bool> dp(allv + 1, 0);//列是所有物品的价值总和
dp[0]=1;//当前0个物品中能组合出价值为0的组合,这是显而易见的
for (int i = 0; i < n; i++)//遍历所有物品
{
for (int j = allv; j >=a[i]; j--)
{
if(dp[j-a[i]]!=0){//前i-1个物品中能够凑出总价值为j-a[i]的组合
dp[j]=true;//那么算上当前物品,在前i个物品中,能够凑出总价值为j的组合
}
}
}
for(int i=x;i<=allv;i++){
if(dp[i]){//找到所有物品能凑出的价值大于x的第一个,就是答案
cout<<i;
return 0;
}
}
}
总结01背包的新功能
其实本质借用了01背包的模板,来求n个物品中取任意个,能组成的价值和,时间复杂度是O(n2 v),n是物品个数,v是物品平均价值.如果使用暴力搜索的话时间复杂度是O(2n ).
在做别的题的时候也遇到过这种零一背包的用法,当时只是看题解.现在总结出来之后感觉明了了很多