P1910 L 国的战斗之间谍

记录101

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int info[1010],wo[1010],mon[1010],dp[1010][1010]; 
int main(){//01背包+双重消耗 
    int n,m,x;
    cin>>n>>m>>x;
    for(int i=1;i<=n;i++) cin>>info[i]>>wo[i]>>mon[i];
    for(int i=1;i<=n;i++){//二维背包 
    	for(int j=m;j>=wo[i];j--){
    		for(int k=x;k>=mon[i];k--){
    			dp[j][k]=max(dp[j][k],dp[j-wo[i]][k-mon[i]]+info[i]);
			}
		}
	}
	cout<<dp[m][x];
    return 0;
}

题目传送门https://www.luogu.com.cn/problem/P1910


突破口

你现在有 N 个人选,每个人都有这样一些数据:A(能得到多少资料)、B(伪装能力有多差)、C(要多少工资)。已知敌人的探查间谍能力为 M(即去的所有人 B 的和要小于等于 M)和手头有 X 元钱,请问能拿到多少资料?


思路

🔍 一、题目核心理解

🎯 问题描述

  • N 个间谍候选人

  • 每人有三个属性:

    • A_i:能获取的资料量(价值)
    • B_i伪装能力差的程度(越小越好)→ 敌人侦察值限制总和 ≤ M
    • C_i工资要求 → 总花费 ≤ X
  • 给定两个资源上限:

    • 总伪装差值 ≤ M(即 ∑B_i ≤ M)
    • 总工资 ≤ X(即 ∑C_i ≤ X)
  • 目标 :在满足上述两个约束的前提下,最大化总资料量 ∑A_i

✅ 这是一个典型的 二维 0-1 背包问题

  • 每个物品(间谍)只能选或不选
  • 有两个"重量"维度:B(伪装差)和 C(工资)
  • "价值"是 A(资料量)

📌 举例(样例)

输入:

复制代码
3 10 12
10 1 11   → 资料10,伪装差1,工资11
1 9 1     → 资料1,伪装差9,工资1
7 10 12   → 资料7,伪装差10,工资12

约束:总 B ≤ 10,总 C ≤ 12

可行方案:

  • 选第1人:B=1≤10,C=11≤12 → 资料=10
  • 选第2人:B=9≤10,C=1≤12 → 资料=1
  • 选1+2:B=1+9=10≤10,C=11+1=12≤12 → 资料=10+1=11
  • 选第3人:C=12≤12,但 B=10≤10 → 资料=7 < 11

→ 最优答案:11


🧠 二、解题思路:二维 0-1 背包 DP

状态定义

  • dp[j][k] = 在 总伪装差 ≤ j总工资 ≤ k 的条件下,能获得的最大资料量

状态转移(0-1 背包)

对每个间谍 i(资料 info[i],伪装差 wo[i],工资 mon[i]):

  • 倒序遍历 jMwo[i]

  • 倒序遍历 kXmon[i]

  • 更新:

    cpp 复制代码
    dp[j][k] = max(dp[j][k], dp[j - wo[i]][k - mon[i]] + info[i])

⚠️ 必须倒序!否则会重复选择同一间谍(变成完全背包)

初始化

  • dp[0..M][0..X] = 0(全局变量自动初始化为 0)
  • 表示:不选任何人时,资料量为 0

答案

  • dp[M][X]:在总伪装差 ≤ M、总工资 ≤ X 下的最大资料量

💡 因为 dp[j][k] 是"不超过 j 和 k"的最大值,所以 dp[M][X] 就是全局最优


代码分析

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int info[1010], wo[1010], mon[1010]; // 存储每个间谍的 A, B, C
int dp[1010][1010];                  // dp[j][k]:伪装差≤j、工资≤k 时的最大资料量
  • 数组大小 1010:因为 M, X ≤ 1000,开 1010 安全(避免越界)
  • info[i] = A_i(资料)
  • wo[i] = B_i(伪装差,"weight of observation")
  • mon[i] = C_i(工资,"money")
cpp 复制代码
int main(){
    int n, m, x;
    cin >> n >> m >> x; // n: 人数;m: 伪装差上限;x: 工资上限
cpp 复制代码
    for(int i = 1; i <= n; i++) 
        cin >> info[i] >> wo[i] >> mon[i];
  • 读入每个间谍的三项数据
  • i=1 开始存储,方便后续循环
cpp 复制代码
    for(int i = 1; i <= n; i++){          // 枚举每个间谍(物品)
        for(int j = m; j >= wo[i]; j--){  // 倒序枚举伪装差上限(第一维容量)
            for(int k = x; k >= mon[i]; k--){ // 倒序枚举工资上限(第二维容量)
                dp[j][k] = max(dp[j][k], dp[j - wo[i]][k - mon[i]] + info[i]);
            }
        }
    }

🔄 三层循环详解:

  1. 外层 i :处理第 i 个间谍
  2. 中层 j(倒序)
    • mwo[i],确保 j - wo[i] ≥ 0
    • 倒序防止重复选择(0-1 背包标准技巧)
  3. 内层 k(倒序)
    • xmon[i],确保 k - mon[i] ≥ 0
    • 同样倒序,保证状态来自"未选当前间谍"的情况

✅ 转移含义:

  • 不选间谍 idp[j][k] 保持不变
  • 选间谍 idp[j - wo[i]][k - mon[i]] + info[i]
  • 取两者最大值
cpp 复制代码
    cout << dp[m][x];
    return 0;
}
  • 直接输出 dp[m][x]:在总伪装差 ≤ m、总工资 ≤ x 下的最大资料量

⚠️ 关键细节说明

细节 说明
为什么倒序? 防止同一个间谍被多次选择(0-1 背包核心)
数组大小 1010? M, X ≤ 1000,开 1010 避免越界(dp[1000][1000] 需要下标到 1000)
初始值为 0? 全局变量自动初始化为 0,正确表示"无间谍可选"
答案是 dp[m][x] 是的,因为 dp[j][k] 表示"不超过 j 和 k"的最大值,单调不减

总结:问题与解法对应

题目要求 DP 设计
每个间谍只能选一次 0-1 背包
两个资源限制(伪装差、工资) 二维背包(dp[j][k]
最大化资料总量 价值 = A_i,求最大总价值
输出最大资料量 dp[M][X]
相关推荐
txzrxz2 小时前
c++深度搜索讲解及例题
开发语言·c++·深度搜索·例题讲解
yu85939582 小时前
时延估计的互相关算法(MATLAB实现)
开发语言·算法·matlab
逸风尊者2 小时前
2026 主流 Claw 类产品技术报告
人工智能·后端·算法
|_⊙2 小时前
红黑树 (C++)
开发语言·c++·学习
楼田莉子2 小时前
同步/异步日志系统:工具类以及日志的简单模块
linux·服务器·数据结构·c++
王老师青少年编程2 小时前
动态规划之【树形DP】第4课:树形DP应用案例实践3
c++·动态规划·dp·树形dp·csp·信奥赛·提高组
强盛机器学习~2 小时前
考虑异常天气和太阳辐射下基于强化学习的无人机三维路径规划
算法·matlab·无人机·强化学习·路径规划·无人机路径规划·q-learning
Pixlout2 小时前
《7元接口体系》v1.0
网络·算法·硬件工程
SUNNY_SHUN2 小时前
不需要Memory Bank:CMDR-IAD用2D+3D双分支重建做工业异常检测,MVTec 3D 97.3%
论文阅读·人工智能·算法·3d