宠物小精灵之收服

一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。小智也想收服其中的一些小精灵。然而,野生的小精灵并不那么容易被收服。对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。当皮卡丘的体力小于等于0时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而使得皮卡丘体力小于等于0的野生小精灵也不会被小智收服。当小智的精灵球用完时,狩猎也宣告结束。

我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。

小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。

现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。请问,小智该如何选择收服哪些小精灵以达到他的目标呢?

输入
输入数据的第一行包含三个整数:N(0 < N < 1000),M(0 < M < 500),K(0 < K < 100),分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。
之后的K行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。
输出
输出为一行,包含两个整数:C,R,分别表示最多收服C个小精灵,以及收服C个小精灵时皮卡丘的剩余体力值最多为R。
样例输入
样例输入1:
10 100 5
7 10
2 40
2 50
1 20
4 20

样例输入2:
10 100 5
8 110
12 10
20 10
5 200
1 110
样例输出
样例输出1:
3 30

样例输出2:
0 100
提示
对于样例输入1:小智选择:(7,10) (2,40) (1,20) 这样小智一共收服了3个小精灵,皮卡丘受到了70点伤害,剩余100-70=30点体力。所以输出3 30

对于样例输入2:小智一个小精灵都没法收服,皮卡丘也不会收到任何伤害,所以输出0 100

题意

明显这是一道简单的二维背包,建立二维数组存储精灵球和伤害值在不同状态下收服的精灵数量

题解

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int i,j,k,l,m,n,ans;
int a[1005],b[1005],d[10005][505];
int main()
{
scanf("%d%d%d",&n,&m,&l);
for (i=1;i<=l;i++)
scanf("%d%d",&a[i],&b[i]);
for (i=1;i<=l;i++)
for (j=n;j>=a[i];j--)//枚举精灵球的数量 
 for (k=m;k>=b[i];k--)//枚举伤害值 
  d[j][k]=max(d[j][k],d[j-a[i]][k-b[i]]+1);//选取数量最多的 
for (i=0;i<=m;i++)//在收服数目相同时,选取最优值 
if (d[n][i]==d[n][m])
{
ans=m-i;
break;
}
printf("%d %d",d[n][m],ans);   
}

<think>好的,我现在需要解决用户关于C++信息学奥赛一本通中第1292题“宠物小精灵收服”的解题思路的问题。首先,我应该回忆一下这道题的题目内容,可能是一个动态规划的问题,因为涉及到资源的最优分配。根据用户提供的引用[2],题目可能涉及宠物小精灵收服,需要处理精灵球的数量和体力值,类似二维背包问题。 接下来,我需要确认题目的具体要求。通常这类题目会给出精灵球的数量限制和皮卡丘的体力限制,每个小精灵需要消耗一定数量的精灵球和体力,目标是收服尽可能多的小精灵,并且在数量相同的情况下,保留尽可能多的体力。这确实符合二维背包问题的特征,即两个维度的限制:精灵球和体力值。 然后,我需要构建动态规划的状态表示。二维背包问题的状态通常是dp[i][j],表示使用i个精灵球和j点体力时能收服的最大小精灵数量。状态转移方程应该是对于每个小精灵k,如果选择收服,那么dp[i][j] = max(dp[i][j], dp[i - balls_needed[k]][j - hp_cost[k]] + 1)。需要注意的是,体力不能为零,所以j的范围可能从1开始。 接下来,处理边界条件和初始化。初始状态应该是dp[0][0] = 0,表示没有使用任何精灵球和体力时收服0个小精灵。然后遍历所有可能的i和j,更新状态。 然后,在计算完所有状态后,需要找到收服最多小精灵的数量,接着在相同数量下寻找剩余体力的最大值。这可能需要遍历dp数组,找到最大值对应的体力消耗情况,然后用总体力减去消耗的体力得到剩余体力,同时确保剩余体力至少为1,因为体力为0的话无法收服。 还要考虑时间和空间复杂度的问题。题目可能有数据量限制,需要确保二维数组的大小足够,并且循环结构不会导致超时。例如,如果精灵球数量上限是1000,体力上限是500,那么二维数组的大小应该是dp[1001][501],这样处理是可行的。 另外,需要参考用户提供的引用[3],其中提到了动态规划的方法,特别是带记忆的递推法和前缀和的运用。虽然那题是关于最大子段和的,但动态规划的思路是相通的,可以借鉴状态转移的方法。 可能需要注意的细节包括:每个小精灵只能被收服一次(01背包),所以需要逆序遍历i和j以避免重复计算。同时,体力消耗不能超过当前体力值,所以在循环时需要从最大值往下减。 最后,验证一下是否有特殊情况,比如当精灵球或体力不足以收服任何小精灵时,输出0和满体力。或者在多个解中选择剩余体力最大的情况。 总结步骤应该是:问题分析转化为二维背包模型,状态定义,状态转移方程,初始化,循环结构,结果提取和优化。这样用户能够逐步理解解题思路,并应用到代码实现中。</think>### C++信息学奥赛一本通 1292题"宠物小精灵收服"解题思路 #### 1. 问题分析 题目属于典型的**二维费用背包问题**,需要同时管理两种资源: - **精灵球数量**(背包第一维度) - **皮卡丘体力值**(背包第二维度) 目标是在有限资源下收服最多的小精灵,且在数量相同的情况下保留最大剩余体力[^2]。 #### 2. 动态规划模型设计 **状态定义**: - $dp[i][j]$ 表示使用$i$个精灵球和$j$点体力时能收服的**最大小精灵数量** **状态转移方程**: $$ dp[i][j] = \max(dp[i][j],\ dp[i - cost\_ball][j - cost\_hp] + 1) $$ 其中$cost\_ball$和$cost\_hp$是收服当前小精灵需要的精灵球和体力值[^3]。 #### 3. 关键实现步骤 1. **初始化**: ```cpp int dp[MAX_BALL][MAX_HP] = {0}; // MAX_BALL=精灵球上限,MAX_HP=体力上限 ``` 2. **递推过程**(逆序遍历防止重复计算): ```cpp for (每个小精灵k) { for (i = 当前最大球数; i >= cost_ball[k]; i--) { for (j = 当前最大体力; j >= cost_hp[k]; j--) { dp[i][j] = max(dp[i][j], dp[i - cost_ball[k]][j - cost_hp[k]] + 1); } } } ``` 3. **结果提取**: - 遍历所有$dp[i][j]$找到最大收服数$max\_cnt$ - 在满足$max\_cnt$的情况下,找到最小体力消耗$min\_hp\_cost$ - 剩余体力为$total\_hp - min\_hp\_cost - 1$(需保证剩余体力≥0) #### 4. 示例代码框架 ```cpp #include <iostream> #include <algorithm> using namespace std; int main() { int ball_total, hp_total, n; // 总球数、总体力、小精灵数量 cin >> ball_total >> hp_total >> n; int dp[1001][501] = {0}; // 根据题目数据范围设定 while (n--) { int cost_ball, cost_hp; cin >> cost_ball >> cost_hp; // 逆序更新二维背包 for (int i = ball_total; i >= cost_ball; --i) for (int j = hp_total - 1; j >= cost_hp; --j) // 体力不能耗尽 dp[i][j] = max(dp[i][j], dp[i - cost_ball][j - cost_hp] + 1); } int max_cnt = 0, min_hp = hp_total; for (int i = 0; i <= ball_total; ++i) for (int j = 0; j < hp_total; ++j) // j < hp_total保证体力不归零 if (dp[i][j] > max_cnt || (dp[i][j] == max_cnt && (hp_total - j) > min_hp)) { max_cnt = dp[i][j]; min_hp = hp_total - j; } cout << max_cnt << " " << min_hp - 1; // 实际剩余体力要减1(如总HP100,消耗50,应剩49) return 0; } ``` #### 5. 关键优化点 - **滚动数组优化**:通过逆序遍历避免重复计算 - **体力边界处理**:始终保留至少1点体力(`j < hp_total`) - **结果筛选策略**:优先保证数量最大,其次选择剩余体力最多
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值