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 背包求最大价值
相关推荐
Qt程序员8 小时前
Linux RCU 原理与应用
linux·c++·内核·linux内核·rcu
想吃火锅10059 小时前
【leetcode】1.两数之和js版
javascript·算法·leetcode
qeen879 小时前
【C++】类与对象之类的默认成员函数(二)
android·c语言·开发语言·c++·笔记·学习
net3m339 小时前
一阶软件低通滤波器算法
人工智能·算法
王老师青少年编程9 小时前
信奥赛C++提高组csp-s之搜索进阶(记忆化搜索案例实践3)
c++·记忆化搜索·方格取数·csp·信奥赛·csp-s·提高组
水木流年追梦10 小时前
大模型入门-大模型优化方法12-YaRN 长文本外推技术
人工智能·分布式·算法·正则表达式·prompt
J-Tony1110 小时前
【JVM】三色标记法
java·jvm·算法
wengad10 小时前
机器学习实践理论基础|算法、模型和数据集
人工智能·算法·机器学习
Titan202411 小时前
Linux动静态库
linux·服务器·c++