题解 P2686 【老虎的题目】
P2686老虎的题目
题目描述&样例输入/输出:这里
似乎本题其他dalao的题解都是注重“怎么做”,那本蒟蒻就试着写一篇解释“为什么”的题解吧
主要思路:dp加上一点前缀和(似乎不用也行?)
凭着良心说,这题作为一道绿的dp,还是有一点水的。
dp嘛,必然要三步走的:
1. 定义状态:
通常来讲我们可能不会一下子就定义对了状态,
但是这题的题目描述太直白了,相当于把怎样定义状态直接告诉了我们。
因为题目要求的是连续的一段(线性),所以我们可以通过只枚举左右两个端点来确定这个区间的大小(长度)。
于是我们把
2.定义状态转移方程
如果是dp,当你找出了状态转移方程之后,你就解决了这个问题的 80%
--沃兹基硕德
看题面,首先这道题存在条件的限制,这告诉我们这道题极有可能有两个及以上的转移方程。
我们首先考虑最正常的情况
首先我们来对这种情况做一个限制条件。
题目要求:题面长度的总和,不能超过high,也不能低于low。
也就是说,我们要求出我们正在考虑的这段区间内所有题目的长度的总和来和
这就用到我们的前缀和了。提前算好前缀和可以让我们用O(1)的时间复杂度来获取当前区间的长度和,否则就得用O(n)的复杂度来算,估计得T掉(没试,不过差不多)
在长度满足要求的情况下,我们可以推出这种情况的转移方程为
(注意,上式的brr代表着“难度”数组的前缀和)
那么问题来了,如果这个区间不符合题目的要求呢??
我们可以把整个枚举长度的过程想成“一个单位一个单位的枚举”
这样的话我们在单位时间里,思考的永远是某一个位置的情况,因此可以得出在不符合要求的情况下的状态转移方程式
3.初始状态定义
本题的初始状态为 这没啥好说的)
剩下的一点小细节,在注释里会提到
#include <iostream>
#include <cstdio>
#define maxn 1005
#define ll long long//要开long long 不然会WA掉#3
using namespace std;
ll arr[maxn];//长度
ll brr[maxn];//难度
ll dp[maxn][maxn];
ll n,low,high;//变量名称与题目一致
int main(){
cin>>n>>low>>high;
int tmp;
for(int i=1;i<=n;i++)cin>>tmp,arr[i]=arr[i-1]+tmp;
for(int i=1;i<=n;i++)cin>>tmp,brr[i]=brr[i-1]+tmp;//输入的同时顺手把前缀和给处理了
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//这里,首先要假设枚举到的位置不成立 (不过关于为什么这一点要在前,我本人只是明白但不会解释,希望有dalao能解释一下QWQ)
if((arr[j]-arr[i-1])>=low&&(arr[j]-arr[i-1])<=high)dp[i][j]=max(dp[i][j],dp[i-1][j-1]+brr[j]-brr[i-1]);//如果符合要求的情况
}
}
cout<<dp[n][n];//输出即可
return 0;
}
总结:代码难度是真的不高,但是确实有几个细节不大好想,一不小心又码了那么多字,有问题请轻喷QAQ