题解:洛谷 P13018 [GESP202506 七级] 调味平衡
本文分享的必刷题目是从蓝桥云课、洛谷、AcWing等知名刷题平台精心挑选而来,并结合各平台提供的算法标签和难度等级进行了系统分类。题目涵盖了从基础到进阶的多种算法和数据结构,旨在为不同阶段的编程学习者提供一条清晰、平稳的学习提升路径。
欢迎大家订阅我的专栏:算法题解:C++与Python实现!
附上汇总贴:算法竞赛备考冲刺必刷题(C++) | 汇总
【题目来源】
洛谷:P13018 [GESP202506 七级] 调味平衡 - 洛谷
【题目描述】
小 A 准备了 n n n 种食材用来制作料理,这些食材依次以 1 , 2 , … , n 1,2,\dots,n 1,2,…,n 编号,第 i i i 种食材的酸度为 a i a_i ai,甜度为 b i b_i bi。对于每种食材,小 A 可以选择将其放入料理,或者不放入料理。料理的酸度 A A A 为放入食材的酸度之和,甜度 B B B 为放入食材的甜度之和。如果料理的酸度和甜度相等,那么料理的调味是平衡的。
过于清淡的料理并不好吃,因此小 A 想在满足料理调味平衡的前提下,合理选择食材,最大化料理的酸度与甜度之和。你能帮他求出在调味平衡的前提下,料理酸度与甜度之和的最大值吗?
【输入】
第一行,一个正整数 n n n,表示食材种类数量。
接下来 n n n 行,每行两个正整数 a i , b i a_i,b_i ai,bi,表示食材的酸度和甜度。
【输出】
输出共一行,一个整数,表示在调味平衡的前提下,料理酸度与甜度之和的最大值。
【输入样例】
3
1 2
2 4
3 2
【输出样例】
8
【核心思想】
-
问题分析:给定 n n n 种食材,每种食材有酸度 a i a_i ai 和甜度 b i b_i bi。需要选择若干食材使得料理的酸度之和 A A A 等于甜度之和 B B B(调味平衡),在此约束下最大化 A + B A + B A+B。这是一个差值背包问题,关键在于将"酸度等于甜度"的约束转化为"差值之和为0"。
-
算法选择:
- 差值转换:设 d i = a i − b i d_i = a_i - b_i di=ai−bi(差值), s i = a i + b i s_i = a_i + b_i si=ai+bi(和值)
- 问题转化:选择食材使 ∑ d i = 0 \sum d_i = 0 ∑di=0,最大化 ∑ s i \sum s_i ∑si
- 带偏移量的01背包:使用偏移量处理负数差值,将差值范围映射到非负数组下标
-
关键步骤:
- 数据转换:对于每种食材,计算 d i = a i − b i d_i = a_i - b_i di=ai−bi, s i = a i + b i s_i = a_i + b_i si=ai+bi
- 状态定义:
f[i][j]表示前 i i i 个食材,差值为 j − O F F S E T j - OFFSET j−OFFSET 时的最大和值 - 偏移量设置:
OFFSET = 50000,将差值范围 [ − O F F S E T , O F F S E T ] [-OFFSET, OFFSET] [−OFFSET,OFFSET] 映射到数组下标 [ 0 , 2 × O F F S E T ] [0, 2 \times OFFSET] [0,2×OFFSET] - 初始化:
f[0][OFFSET] = 0(差值为0时和值为0),其余为负无穷 - 状态转移:
- 不选食材 i i i:
f[i][j] = f[i-1][j] - 选食材 i i i:
f[i][j] = max(f[i][j], f[i-1][j-d[i]] + s[i])(要求 j ≥ d [ i ] j \geq d[i] j≥d[i])
- 不选食材 i i i:
- 答案获取:
f[n][OFFSET]即差值为0时的最大和值
-
时间/空间复杂度:
- 时间复杂度: O ( n × M ) O(n \times M) O(n×M),其中 M = 2 × O F F S E T = 100000 M = 2 \times OFFSET = 100000 M=2×OFFSET=100000
- 空间复杂度: O ( n × M ) O(n \times M) O(n×M),可优化至 O ( M ) O(M) O(M)(滚动数组)
-
差值背包的核心思想:
- 差值转换技巧:将"两个属性相等"的约束转化为"差值之和为0"
- 偏移量处理:通过加偏移量将可能为负的差值映射到非负数组下标
- 01背包模型:每个食材选或不选,目标是在差值约束下最大化和值
- 适用于"两个属性相等约束下最大化和"的背包问题
【算法标签】
#普及 #01背包
【代码详解】
#include<iostream>
#include<cstring>
using namespace std;
const int N=105, M = 1000005 ; // 定义最大物品数N和最大容量M
int n, f[N][M],d[N],s[N]; // 物品数n,DP数组f,差值数组d,和值数组s
int main(){
memset(f,-0x3f3f3f,sizeof f); // 初始化DP数组为极小值
cin>>n; // 输入物品数量
f[0][50000]=0; // 初始化基础状态,偏移量50000
for (int i=1; i<=n; i++) // 读取每个物品的数据
{
int a, b; // 物品的两个属性
cin >> a >> b; // 输入a和b
d[i] = a - b; // 计算差值
s[i] = a + b; // 计算和值
}
for (int i=1;i<=n;i++) // 遍历每个物品
{
for (int j=d[i];j<=100000;j++) // 遍历所有可能的容量
{
f[i][j]=max(f[i-1][j],f[i-1][j-d[i]]+s[i]); // 状态转移方程
}
}
cout<<f[n][50000]; // 输出结果
return 0; // 程序正常结束
}
【运行结果】
3
1 2
2 4
3 2
8
更多推荐


所有评论(0)