最近对于序列变换这类题的解法有一种心得,想分享给大家。
给大家三个题,其实是两个题,后两个题是一样的。把两个题放在一起,这样可以让你对这类题目有更深刻的理解。
牛客练习赛16 - B题:漂亮的树
题目链接:点这儿。
这个题是要把序列变成这个形式:a, a + 1, a + 2, ...a + n / 2, a + n / 2 - 1, ... a + 1, a
。
如果,给出的序列本身就是这种形式,那么,把两个序列相减,就会得到b, b, ..., b
,也就是得到一串相同的数字,那么再想一下,如果这个序列只有一个位置不符合规则,把两个序列相减就会得到b, b, ..., c, b, ..., b
,那么这个序列中就只有有一个数字与众不同;
因此得出结论,把两个序列相减,相同数字最多的那些位置是不需要变动的,只需要把那些不相同的数字的位置的值变成符合条件就行了。
这样子,我们不仅得到了最少需要改变的数量,而且还得到了具体需要改变的位置。
#include <bits/stdc++.h>
using namespace std;
int main()
{
for (int n; EOF != scanf("%d", &n); ) {
vector<int> arr(n);
for (int i = 0; i < n; scanf("%d", &arr[i++])) {}
map<int, int> mp;
for (int i = 0; i < n / 2; ++mp[arr[i] - i - 1], ++i) {}
for (int i = n - 1; i >= n / 2; ++mp[arr[i] - n + i], --i) {}
struct cmp {
bool operator () (const pair<int, int> &A, const pair<int, int> &B) const {
return B.second > A.second;
}
};
printf("%d\n", n - max_element(mp.begin(), mp.end(), cmp())->second);
}
return 0;
}
最长上升子序列
HDU5256 - 序列变换
题目链接:点这儿。
这一题和上一题差不多,都是把最少改动转化成最多不需要改动。
和上一题一样的分析方法,要把序列变成严格递增的,这里我们手动构造一组严格递增序列:0, 1, 2, ..., n
,而如果我们的序列就是严格递增的,把目标序列减去这个递增序列,看下差值序列,可以发现,差值序列也一定是递增的,只不过这个差值序列可能是非严格递增的;
而差值序列非严格递增的部分是不需要我们去改动的,因此,这题就是要求差值序列的最长不下降子序列的长度。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T, K = 0, n;
for (scanf("%d", &T); T--; ) {
scanf("%d", &n);
vector<int> arr;
int ans = 0;
for (int i = 0, x; i < n; scanf("%d", &x), arr.push_back(x - i++)) {}
vector<int> dp;
for (auto it = arr.begin(); it != arr.end(); ++it) {
auto it2 = upper_bound(dp.begin(), dp.end(), *it);
if (it2 == dp.end())
dp.push_back(*it);
else
*it2 = *it;
}
printf("Case #%d:\n%d\n", ++K, ans + arr.size() - dp.size());
}
return 0;
}
51NOD1294 - 修改数组
题目链接:点这儿。
和上一个题一样,只不过多了个条件:序列是正整数。上一个题没这个要求。
我们分析下,假设有一个序列:2, 3, 2, 2
,那么后面两个数字一定要改掉,因为就算这个序列最后变成1, 2, 3, 4
,原序列中的2 < 3
,2 < 4
,也就是说当一个序列中的一些数字不符合条件(arr[i] - i <= 0
)一定要改掉时,我们就把这些数字从序列中剔除掉,剩下的数字构成的序列就运用上一题的方法做就行了。
#include <bits/stdc++.h>
using namespace std;
int main()
{
for (int n; EOF != scanf("%d", &n); ) {
vector<int> arr;
int ans = 0;
for (int i = 0, x; i < n; i++) {
scanf("%d", &x);
if (x - i > 0)
arr.push_back(x - i);
else
++ans;
}
vector<int> dp;
for (auto it = arr.begin(); it != arr.end(); ++it) {
auto it2 = upper_bound(dp.begin(), dp.end(), *it);
if (it2 == dp.end())
dp.push_back(*it);
else
*it2 = *it;
}
printf("%d\n", ans + arr.size() - dp.size());
}
return 0;
}
总结
一定要把目标序列和当前序列放在一起看一下,最好做个差值,就可以找到之中的一些规律:把最少改动转化成最多不需要改动。