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 背包求最大价值
相关推荐
Rabitebla2 小时前
【数据结构】实现通讯录:基于C语言动态顺序表
c语言·开发语言·数据结构·算法
tankeven2 小时前
动态规划专题(06):树形动态规划(未完待续)
c++·算法·动态规划
满天星83035772 小时前
【Linux/多路复用】poll和epoll的使用
linux·服务器·c++·后端
睡觉就不困鸭2 小时前
第13天 四数相加II
数据结构·哈希算法·散列表
米粒12 小时前
力扣算法刷题 Day 52
算法·leetcode·职场和发展
今儿敲了吗2 小时前
应用实战2:新闻列表
学习·算法
waves浪游2 小时前
进程间通信(上)
linux·运维·服务器·开发语言·c++
珎珎啊2 小时前
Python3 数据结构
数据结构·python
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 102. 二叉树的层序遍历 | C++ 极简 DFS 巧解
c++·leetcode·深度优先