✏️ 纠错
第 137 题 / 共 155 题

题目描述

小 A 准备了 n 种食材用来制作料理,这些食材依次以 1,2,…,n 编号,第 i 种食材的酸度为 ai​,甜度为 bi​。对于每种食材,小 A 可以选择将其放入料理,或者不放入料理。料理的酸度 A 为放入食材的酸度之和,甜度 B 为放入食材的甜度之和。如果料理的酸度和甜度相等,那么料理的调味是平衡的

过于清淡的料理并不好吃,因此小 A 想在满足料理调味平衡的前提下,合理选择食材,最大化料理的酸度与甜度之和。你能帮他求出在调味平衡的前提下,料理酸度与甜度之和的最大值吗?

输入格式

第一行,一个正整数 n,表示食材种类数量。

接下来 n 行,每行两个正整数 ai​,bi​,表示食材的酸度和甜度。

输出格式

输出共一行,一个整数,表示在调味平衡的前提下,料理酸度与甜度之和的最大值。

输入输出样例

输入 #1

3
1 2
2 4
3 2

输出 #1

8

输入 #2

5
1 1
2 3
6 1
8 2
5 7

输出 #2

2

说明/提示

对于 40% 的测试点,保证 1≤n≤10,1≤ai​,bi​≤10。

对于另外 20% 的测试点,保证 1≤n≤50,1≤ai​,bi​≤10。

对于所有测试点,保证 1≤n≤100,1≤ai​,bi​≤500。

📝 题目解析

【题目大意】给定n种食材,每种食材有酸度a_i和甜度b_i。可以选择若干食材放入料理,要求料理的总酸度A等于总甜度B,并且在满足这个条件的前提下,最大化A+B的值。需要计算这个最大的A+B值。(1≤n ≤100,1≤ai,bi≤500)

【考纲知识点】

简单动态规划(一维动态规划、简单背包问题)

【解题思路】

问题转化:本质上是一个变种的背包问题,需要找到一组食材,使得它们的总酸度和总甜度相等,并且这个总和的2倍(因为A+B=A+A=2A)尽可能大。

动态规划:使用动态规划来记录可能的酸度和甜度差值。定义dp[i][j]表示前i种食材,酸度与甜度的差值为j时,能达到的最大总酸度(或总甜度)。

状态转移:对于每种食材,考虑选或不选:

不选:状态不变。

选:更新差值j和总酸度。

初始状态:dp[0][0] = 0,表示初始时差值为0,总和为0。

目标状态:dp[n][0]

【参考程序】
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 105;
const int C = 505;
const int OFFSET = N * C;  // 偏移量,用于处理负索引
const int D = 2 * OFFSET;  // 数组总大小

int n;
int f[D];

int main() {
    scanf("%d", &n);
    // 初始化数组,设置一个极小值表示不可达状态
    for (int i = 0; i < D; i++)
        f[i] = -1e9;
    // 初始状态:选择0种食材时,差值为0,贡献和为0
    f[OFFSET] = 0;
    
    while (n--) {
        int a, b;
        scanf("%d%d", &a, &b);
        int x = a + b;  // 该食材的总贡献
        int y = a - b;  // 该食材的酸甜差值
        
        if (y <= 0) {
            // 处理非正差值,从左到右遍历,避免重复使用同一食材
            // 限制遍历范围,确保i + y在有效范围内
            for (int i = -y; i < D; i++) {
                if (f[i] != -1e9) {  // 只处理可达状态
                    f[i + y] = max(f[i + y], f[i] + x);
                }
            }
        } else {
            // 处理正差值,从右到左遍历,避免重复使用同一食材
            // 限制遍历范围,确保i在有效范围内
            for (int i = D - 1; i >= y; i--) {
                if (f[i - y] != -1e9) {  // 只处理可达状态
                    f[i] = max(f[i], f[i - y] + x);
                }
            }
        }
    }
    
    // 输出差值为0时的最大贡献和,如果不可达则输出0
    printf("%d\n", max(f[OFFSET], 0));
    return 0;
}

第二种方法:


#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 105;
const int M = 100000;   // 差值范围[-50000,50000]
const int base = 50000;   // 偏移量

int n;
int a[N], b[N];
int f[105][100005];   // f[i][j]:前i个食材,差值(加上偏移量)为j的最大总和。

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &a[i], &b[i]);
    }

    memset(f, -0x3f, sizeof(f));
    f[0][base] = 0;

    for (int i = 1; i <= n; i++) {
        int d = a[i] - b[i];
        int s = a[i] + b[i];
        for (int j = 0; j <= M; j++) {
            f[i][j] = f[i-1][j];   // 不选第i个
            if (j - d >= 0 && j - d <= M) {
                f[i][j] = max(f[i][j], f[i-1][j-d] + s);
            }
        }
    }

    printf("%d\n", f[n][base] > 0 ? f[n][base] : 0);
    return 0;
}