P2722 [USACO3.1] 总分 Score Inflation

记录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):

  • 因为可以无限选 ,所以对每个时间 jtm

    cpp 复制代码
    dp[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 m
    • dp[j - t] 可能已经使用了当前类题目(因为 j-t < j,且已更新)
    • 所以 dp[j] 可以包含多个当前类题目 → 实现"无限选择"

✅ 举例:若 t=20, p=35

  • j=20dp[20] = max(0, dp[0]+35) = 35
  • j=40dp[40] = max(0, dp[20]+35) = 70 → 相当于选了 2 道
  • j=60dp[60] = 105 → 3 道,依此类推
cpp 复制代码
    cout << dp[m]; // 输出最大得分
    return 0;
}

⚠️ 关键细节说明

细节 说明
正序遍历 完全背包的核心特征,允许重复选择同一类题目
初始化为 0 正确,因为不选任何题得 0 分
空间优化 使用一维数组,空间 O(m),时间 O(n×m) ≈ 1e8,在 C++ 中可接受
无后效性 每次只依赖更小的时间状态,DP 正确

总结:问题与解法对应

题目要素 DP 设计
多类题目,每类无限选 完全背包
时间限制 m 背包容量
每类题:耗时 t,得分 p 物品重量和价值
最大化总分 max 转移
正序更新 允许重复选择
相关推荐
民乐团扒谱机1 小时前
【源码剖析】MATLAB混响函数底层逻辑拆解:Dattorro算法从公式到音频帧的完整推导
算法
淡海水1 小时前
【AI模型】概念-Token
人工智能·算法
凯瑟琳.奥古斯特2 小时前
数据结构核心知识点精要
数据结构·算法·排序算法
t***5442 小时前
如何在 Dev-C++ 中配置 Clang 编译器集
开发语言·c++
隔壁大炮2 小时前
Day02-04.张量点乘和矩阵乘法
人工智能·pytorch·深度学习·线性代数·算法·矩阵
王老师青少年编程2 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【删数问题】:删数问题
c++·算法·贪心·csp·信奥赛
海清河晏1112 小时前
数据结构 | 链栈
数据结构
geneculture2 小时前
本真信息观:基于序位守恒的融智学理论框架——人类认知第二次大飞跃的基础
人工智能·算法·机器学习·数据挖掘·融智学的重要应用·哲学与科学统一性·融智时代(杂志)
kronos.荒2 小时前
动态规划——最长递增子序列系列问题(python)
算法·动态规划·最长递增子序列系列问题