记录111
cpp
#include<bits/stdc++.h>
using namespace std;
int dp[10010];//时间状态
int main(){
int m,n,p,t;
cin>>m>>n;
for(int i=1;i<=n;i++){//遍历题目
cin>>p>>t;//分数,时间
for(int j=t;j<=m;j++){//正序更新,重新寻找
dp[j]=max(dp[j],dp[j-t]+p);//更新为最大值
}
}
cout<<dp[m];
return 0;//结束程序
}
题目传送门
https://www.luogu.com.cn/problem/P2722
突破口
我们可以从几个种类中选取竞赛的题目,这里的一个"种类"是指一个竞赛题目的集合,解决集合中的题目需要相同多的时间并且能得到相同的分数。
你的任务是写一个程序来告诉 USACO 的职员,应该从每一个种类中选取多少题目,使得解决题目的总耗时在竞赛规定的时间里并且总分最大。
🔍 一、题目核心理解
🎯 问题描述
- 竞赛总时间限制为
m - 有
n类题目,每类题目:- 解一道得
p_i分 - 耗时
t_i - 可以重复选择(无限道)
- 解一道得
- 目标:在总时间 ≤
m的前提下,最大化总得分
✅ 这是一个典型的 完全背包问题(Unbounded Knapsack):
- 背包容量 = 总时间
m- 每个物品(题目类)可以选任意多次
- 物品重量 = 时间
t_i,价值 = 分数p_i- 求最大总价值
📌 输入样例解析(输入 #1)
cpp
300 4 → 总时间 300,4 类题目
100 60 → 类1:60分钟得100分
250 120 → 类2:120分钟得250分
120 100 → 类3:100分钟得120分
35 20 → 类4:20分钟得35分
最优策略:
- 选 15 道第4类题 :15 × 20 = 300 分钟,得分 15 × 35 = 525 ❌
- 更优:选 2 道类2(2×120=240分钟,500分) + 3 道类4(3×20=60分钟,105分) → 总时间 300,总分 605 ✅
输出:605
🧠 二、解题思路:完全背包动态规划
状态定义
dp[j]表示:在总时间恰好为 j 时,能获得的最大分数
💡 注意:虽然定义是"恰好 j",但由于我们从 0 开始且只做 max,最终
dp[m]就是"≤m"的最大值。
状态转移
对每一类题目(分数 p,时间 t):
-
因为可以无限选 ,所以对每个时间
j从t到m:cppdp[j] = max(dp[j], dp[j - t] + p);
关键:正序遍历
- 完全背包必须正序遍历容量 (
j = t to m) - 这样
dp[j - t]可能已经包含了当前类题目,从而允许多次选择
✅ 对比 0-1 背包:倒序防止重复;完全背包:正序允许重复
初始化
- 全局
int dp[]自动初始化为 0 dp[0] = 0(0 时间得 0 分),其余初始为 0,正确
最终答案
dp[m]:在不超过m时间下的最大得分
cpp
#include<bits/stdc++.h>
using namespace std;
int dp[10010]; // dp[j]:使用 j 单位时间能获得的最大分数(m ≤ 10000)
- 数组大小
10010>m_max = 10000,安全
cpp
int main(){
int m, n, p, t;
cin >> m >> n; // 读入总时间 m 和题目类数 n
cpp
for(int i = 1; i <= n; i++){ // 遍历每一类题目
cin >> p >> t; // 读入该类题的分数 p 和耗时 t
cpp
for(int j = t; j <= m; j++){ // 正序遍历时间(完全背包关键!)
dp[j] = max(dp[j], dp[j - t] + p); // 更新最大分数
}
}
🔄 核心逻辑说明:
- 外层 :处理第
i类题目 - 内层正序 :
j = t to mdp[j - t]可能已经使用了当前类题目(因为j-t < j,且已更新)- 所以
dp[j]可以包含多个当前类题目 → 实现"无限选择"
✅ 举例:若
t=20, p=35
j=20:dp[20] = max(0, dp[0]+35) = 35j=40:dp[40] = max(0, dp[20]+35) = 70→ 相当于选了 2 道j=60:dp[60] = 105→ 3 道,依此类推
cpp
cout << dp[m]; // 输出最大得分
return 0;
}
⚠️ 关键细节说明
| 细节 | 说明 |
|---|---|
| 正序遍历 | 完全背包的核心特征,允许重复选择同一类题目 |
| 初始化为 0 | 正确,因为不选任何题得 0 分 |
| 空间优化 | 使用一维数组,空间 O(m),时间 O(n×m) ≈ 1e8,在 C++ 中可接受 |
| 无后效性 | 每次只依赖更小的时间状态,DP 正确 |
总结:问题与解法对应
| 题目要素 | DP 设计 |
|---|---|
| 多类题目,每类无限选 | 完全背包 |
| 时间限制 m | 背包容量 |
| 每类题:耗时 t,得分 p | 物品重量和价值 |
| 最大化总分 | max 转移 |
| 正序更新 | 允许重复选择 |