P2430 严酷的训练

记录105

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int dp[5010];
int ti[110];
int main(){	
	int a,a1,a2,m,n;//二人比例,同学水平,老师水平,题目数,知识数 
	cin>>a1>>a2;//同学水平,老师水平
	a=a2/a1;//二人比例 
	cin>>m>>n;//题目的总数m 和知识点的总数n
	int t;//对应知识点的时间 
	for(int i=1;i<=n;i++){//n个知识点
		cin>>t;//输入时间 
		ti[i]=t*a;//同学做这个知识点的时间 
	}
	int p,q;//p表示该题目所属的知识点,q表示该题目对应的奖励值 
	for(int i=1;i<=m;i++){//m个题目 
		cin>>p>>q;//输入 
		for(int j=5000;j>=ti[p];j--){//背包装 
			dp[j]=max(dp[j],dp[j-ti[p]]+q);//一直装下去 
		}
	}
	int num;//规定时间 
	cin>>num;//输入 
	cout<<dp[num];//输出 
	return 0;//结束程序 
}

题目传送门https://www.luogu.com.cn/problem/P2430


突破口

WKY 和老王都有一个水平值,他们水平值的比值和做这些题所用时间的比值成反比。比如如果 WKY 的水平值是 1,老王的水平值是 2,那么 WKY 做同一道题的时间就是老王的 2 倍。

现在 WKY 同学请你帮忙,计算在老王规定的时间内,WKY 所能得到最大奖励值是多少 。


🔍 一、题目核心理解

🎯 问题描述

  • m 道题目 ,每道题属于某个知识点 p(1 ≤ p ≤ n)
  • 每个知识点 i 有一个 老王做该类题的时间 t_i
  • WKY 的水平值为 a1,老王为 a2,且 a2a1 的整数倍
  • WKY 做某知识点题的时间 = 老王时间 × (a2 / a1)
    • 因为:水平比 = a1 : a2 → 时间比 = a2 : a1(反比)

✅ 举例:WKY 水平=1,老王=2 → WKY 花费时间是老王的 2 倍

  • 每道题有一个奖励值 q
  • 给定总时间上限 num
  • 目标 :选择若干题目,在总时间 ≤ num 的前提下,最大化总奖励值

💡 注意:同一知识点下的不同题目,对 WKY 来说耗时相同(因为只依赖知识点)

→ 这是一个标准的 0-1 背包问题

  • 每道题是一个物品
  • "重量" = 该题所属知识点的 WKY 耗时
  • "价值" = 奖励值 q
  • 背包容量 = 总时间上限

📌 输入样例解析(输入 #1)

cpp 复制代码
1 2        → a1=1, a2=2 → 比例 a=2
6 4        → m=6题, n=4知识点
1 2 3 4    → 老王做各知识点时间
题目:
1 5   → 知识点1,奖励5 → WKY耗时=1×2=2
2 6   → 耗时=2×2=4,奖励6
3 3   → 耗时=3×2=6,奖励3
4 8   → 耗时=4×2=8,奖励8
3 3   → 耗时=6,奖励3
4 5   → 耗时=8,奖励5
20        → 总时间上限

可能的最优选择:

  • 选题1(2,5)、题2(4,6)、题3(6,3)、题5(6,3)、题6(8,5)
  • 总时间 = 2+4+6+6+8 = 26 > 20 ❌

尝试:

  • 题1(2,5) + 题2(4,6) + 题4(8,8) + 题5(6,3) → 2+4+8+6=20,奖励=5+6+8+3=22 ✅

输出:22


🧠 二、解题思路:0-1 背包

步骤

  1. 读入 WKY 和老王的水平值,计算时间放大比例 a = a2 / a1
  2. 读入每个知识点的老王耗时,乘以 a 得到 WKY 耗时 ti[i]
  3. 对每道题:
    • 知道它属于知识点 p
    • 它的"重量" = ti[p]
    • "价值" = q
  4. 做标准 0-1 背包:
    • dp[j] = 在时间 j 内能获得的最大奖励
    • 倒序更新
  5. 输出 dp[num]

代码分析

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int dp[5010];   // dp[t] = 时间t内能获得的最大奖励(时间上限≤5000)
int ti[110];    // ti[i] = WKY做知识点i的题目所需时间(n≤100)
  • dp 大小 5010:因规定时间 ≤5000
  • ti 大小 110:因 n ≤100
cpp 复制代码
int main(){	
    int a, a1, a2, m, n;
    cin >> a1 >> a2; // WKY水平,老王水平
    a = a2 / a1;     // 时间放大比例(整数)
  • 题目保证 a2a1 的整数倍 → a 为整数
cpp 复制代码
    cin >> m >> n; // 题目总数m,知识点总数n
cpp 复制代码
    int t;
    for(int i = 1; i <= n; i++){
        cin >> t;          // 老王做知识点i的时间
        ti[i] = t * a;     // WKY做该知识点题的时间
    }
  • 预处理每个知识点的 WKY 耗时
cpp 复制代码
    int p, q;
    for(int i = 1; i <= m; i++){
        cin >> p >> q; // 题目所属知识点p,奖励q
        for(int j = 5000; j >= ti[p]; j--){
            dp[j] = max(dp[j], dp[j - ti[p]] + q);
        }
    }

🔄 背包更新:

  • 每道题视为一个独立物品
  • "重量" = ti[p](该题所属知识点的 WKY 耗时)
  • "价值" = q
  • 倒序遍历时间 j,防止重复选择(0-1 背包标准做法)

✅ 即使多个题目属于同一知识点,它们的"重量"相同,但仍是不同物品,可分别选或不选

cpp 复制代码
    int num;
    cin >> num; // 规定的时间上限
    cout << dp[num]; // 输出最大奖励
    return 0;
}
  • dp[num] 表示在时间 ≤ num 下的最大奖励(因为 DP 是"不超过容量"的最大值)

⚠️ 关键细节说明

细节 说明
dp 初始化? 全局数组 → 自动初始化为 0,正确(不选任何题,奖励为 0)
为什么倒序? 防止同一题目被多次选择(虽然是不同题,但若正序,可能在一轮中多次更新)
同一知识点多题? 没问题!每道题独立处理,即使耗时相同,也是不同物品
时间放大比例? a = a2 / a1,题目保证整除,无精度问题

总结:问题与解法对应

题目要素 背包模型
每道题 物品
WKY 做题时间 重量(由知识点决定)
奖励值 价值
总时间上限 背包容量
最大化奖励 0-1 背包求最大价值
相关推荐
法哥201223 分钟前
用 C++ 控制 CMW100,到底在干什么?
开发语言·c++
典典分享指南41 分钟前
VS Code Git 工作树:解锁多分支并行开发新体验
算法·决策树·逻辑回归·启发式算法
_Narcissus_1 小时前
枚举和模拟算法笔记
c语言·数据结构·c++·笔记·算法·模拟·枚举
天疆说1 小时前
策略的进化:从随心所欲到稳健前行
算法
冻柠檬飞冰走茶1 小时前
《数据结构实验指导-C++语言版》 在顺序表 list 中查找元素 x
开发语言·数据结构·c++·算法·list
Brilliantwxx1 小时前
【C++】 高阶数据结构图(1)并查集
开发语言·数据结构·c++
小鱼仙官2 小时前
Ubuntu C++ NTP校时程序
linux·c++·ubuntu
冻柠檬飞冰走茶2 小时前
《数据结构实验指导-C++语言版》 返回单链表 list 中第 i 个元素值
开发语言·数据结构·c++·算法·list
happyprince2 小时前
03-深刻观-CodeX哲学与升华(源码)
算法·ai编程
不会打球的王子3 小时前
Day 27:迁移学习与微调 — 站在巨人的肩膀上
算法