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 转移
正序更新 允许重复选择
相关推荐
黄敬峰2 小时前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术4 小时前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六7 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术8 小时前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize8 小时前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考1 天前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
CSharp精选营1 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
美团技术团队1 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode