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 转移
正序更新 允许重复选择
相关推荐
青少儿编程课堂11 分钟前
后缀自动机解析:本质不同子串与最长重复片段统计
c++·python·算法·bfs·信息学竞赛
朝朝辞暮i11 分钟前
C++ 第 12 课:局部变量、作用域、变量生命周期
开发语言·c++·算法
乐迪信息2 小时前
港口船舶逆行怎么管?AI防爆摄像机搭载智能检测算法
大数据·人工智能·深度学习·算法·计算机视觉
无名猿2 小时前
shared_ptr 完全指南:引用计数、控制块与开销
c++·性能优化·内存管理·标准库·现代c++
Sirens.3 小时前
Java并发锁详解:六类锁策略与 synchronized 底层原理
java·前端·算法
和裕3 小时前
全纸结构重型纸箱能否满足 1 吨以上设备出口熏蒸豁免要求?通关合规性全解析
大数据·运维·网络·人工智能·算法
lvwangshu3 小时前
T2 最长上升子序列(lis)题解
动态规划·题解·技巧·性质题·思维好题
tangguofeng3 小时前
不带头结点的链队操作集(不含InitFlag)(C语言版)
算法
lvwangshu4 小时前
T1 称重(weight)题解
题解·贪心·总结·技巧·性质题·思维好题