题解:B3873 [GESP202309 六级] 小杨买饮料
Clouds_dream · · 题解
前置知识
01背包——OI wiki
题目大意
题目传送门
题目分析
有
优化
第一,如果
第二,我们可以发现背包的容量为
证明背包容量为 2L-1 (反证法)
-
*假设存在最优解 $S^\geq 2L
** 设 S^是最优解的总容量,且 S^\geq 2L,对应的最小花费为 C^*$ 。 -
移除部分物品
由于每个物品的容量l_i\leq L (经过剪枝优化),我们可以从S^* 中逐个移除物品,直到剩余容量S' 满足S'<2L 。
移除过程中,剩余容量始终\geq L (初始S^* \geq 2L ,每次移除物品l_i \leq L ,故S^* - l_i \geq 2L - L = L )。 -
花费对比
设移除物品后的总花费为C' ,则:C' \leq C^* - \sum_{\text{removed}} c_i \leq C^* 所以不等号成立:因
c_i \geq 0 (物品花费非负),移除物品不会增加花费。 -
剩余容量范围
移除过程结束时:S' \in [L,\, 2L-1] \quad \text{ (因 $S' < 2L$ 且 $S' \geq L$)} -
推出矛盾
-
- 但
C' \leq C^* 与 "C^* 是最小花费"矛盾(若C' < C^* ,则C^* 非最优;若C' = C^* ,则S' 是更优解,因其容量更接近L )。
-
故结论得证。
继续分析
然后我们就可以按照上面的思路开始 DP 了,然后从
无解情况:
-
所有饮料之和都不能满足需求,就输出 ```no solution``` 。 -
把
ans 初始化为一个大值,如果遍历完dp 数组ans 仍为最大值,则也无解。
还有,注意
答疑
Q:为什么要从
A:因为只有大于等于
5 100
1 2000
10 100
20 80
40 20
10000 1
购买的饮料总容量可能大于
代码实现
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define TRACE 1
#define tcout TRACE && cout
#define int long long
//十年OI一场空,不开long long见祖宗(其实本题没必要)
#define fst ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);//快读
#define pri priority_queue
const int P = 998244353;
const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6+10, M = 1e8 + 10;
int n,L;//n为物品数量,L为需要的数量
int c[N],l[N];//含义见题目
int dp[N];
int cnt=0;//饮料总量
signed main()
{
fst
memset(dp,0x3f,sizeof(dp));//数组初始化
cin>>n>>L;
for(int i=1;i<=n;i++){
cin>>c[i]>>l[i];
cnt+=l[i];
l[i]=min(l[i],L);
}
if(cnt<L){
cout<<"no solution";//无解1
return 0;
}
dp[0]=0;
for(int i=1;i<=n;i++){
for(int j=2*L;j>=l[i];j--){
dp[j]=min(dp[j],dp[j-l[i]]+c[i]);//板子
}
}
int ans=INF;
for(int i=L;i<=2*L;i++){
ans=min(ans,dp[i]);
}
if(ans==INF){
cout<<"no solution";//无解2
return 0;
}
cout<<ans;
return 0;
}
UPD
2025/6/2 改正了
2025/6/2 证明了背包容量为