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

  • 因为可以无限选 ,所以对每个时间 j 从 t 到 m:

    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=20:dp[20] = max(0, dp[0]+35) = 35
  • j=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 转移
正序更新 允许重复选择
相关推荐
好悬给我拽开线2 小时前
ZILOT 论文解析
算法
kloc2 小时前
聊天室项目入口网关子服务的实现
linux·c++
饮水桶_T3 小时前
JAVA IO流笔记
开发语言·数据结构·算法
星夜离尘564 小时前
MATLAB 离散数据分箱:半开区间、末端点与计数、加权和、平均
算法·matlab
Pniubi4 小时前
力扣76.最小覆盖子串
算法·leetcode·职场和发展
地平线开发者5 小时前
征程6|用 hrt\_model\_exec 看清 YOLOv5x 的输入、原始输出和后处理
人工智能·算法
csdn_aspnet5 小时前
C++ 3个有序点的方向(Orientation of 3 ordered points)
c++·算法
赤生云电竞6 小时前
GPU算力共享平台架构实践:任务分发与资源隔离的关键配置
算法·架构·gpu算力
小七在进步6 小时前
string的使用(2)
c++
青少儿编程课堂7 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:折半搜索优化子集枚举(Meet in the Middle)解析
c++·python·算法·bfs·信息学竞赛