又是一道根本看不出来是dp的dp题。。。
这道题的dp表示为:dpij,i含义是要组成的数字,j含义是组成i的左端点,dpij整体的含义是组成数字i的右端点,特别注意,左端点为闭区间,右端点为开区间。
知道了这些后,来看看dp推导式,两个i-1能组成一个i的原理,我们找到两个连续的i-1才能拼成一个i,假设拼成i-1数字的左端点为j,则第一个i-1数字右端点就是dpi-1j,知道了左右端点就可知哪一部分是i-1了。第二个数字怎么办呢?因为是相邻,所以肯定是以上一个数字紧挨着来找了,所以是以dpi-1j为左端点,dp【i-1】【dp【i-1】【j】】为右端点。这个右区间也就是合成i的右区间。
为什么还以dpi-1j为左端点,上一次合成第一个i-1不是用过了吗?这就是为什么我们说右区间为开区间。
整体式子为dp[i][j] = dp[i-1][dp[i-1][j]];
代码如下
#include <iostream>
using namespace std;
const int N = 262200;
int a[N], f[60][N], n;
int main()
{
cin >> n;
for (int i = 1; i <= n; ++ i ) {
int x; cin >> x;
f[x][i] = i + 1;
}
int ans = 2;
for (int i = 2; i <= 60; ++ i )
for (int j = 1; j <= n; ++ j )
{
if (!f[i][j])
f[i][j] = f[i - 1][f[i - 1][j]];
if (f[i][j]) ans = i;
}
cout << ans << endl;
return 0;
}
为什么录入数据时候,右端点为i+1,这个也和右端点是开区间有关
还有注意的是,进行dp时候,如果要组成的数字没有右端点,那么我们就取一下看看能不能取到右端点,然后得到答案了后不要写else,这样会错过答案也要写成if判断。
如果要取的数字本来就有就会跳过第一个查找而直接更新答案,还有就是为什么我们凑成的数字i只到60呢?
这是因为即使有262144个数字,但是每个数字最多只有40这么大,即使有262144个40,那么也只能凑成10w多41,而这10w多41,又凑成5w多42,以此类推发现根本凑不出超过60的数字