A. Make All Equal
题意:给定n个数每次可以选2个相邻的数,并且前面的数不能比后面的数大,并且删除其中的一个。这个数组是循环数组,最后一个数是第一个数的前一个数。问最少操作多少次,可以让剩下的数全都相等。
思路:红黑树+一次遍历,记录出现次数最多的数,剩下的数全部删掉即可。
总结:看了一下示例很快就A了,但是不太理解这里要求选数的时候 前面的数不能比后面的数大 这个条件有什么影响。 思考一下,假设有相邻的i,j。i出现的次数最多,i > j,则不能直接删除j,需要考虑j和j + 1,而j + 1和j的大小关系不确定,如果j + 1是要保留的数,那么可以直接删掉j,如果不是要保留的数,那么不在乎大小关系,随便删一个即可。所以到了最后,j的后面的数一定是i,另一种情况当i <= j,直接删掉j。
void solve(){
int n;
cin >> n;
map<int, int> mapp;
int ans = n + 1;
for (int i = 0; i < n; ++i) {
int t;
cin >> t;
mapp[t] ++;
ans = min(ans, n - mapp[t]);
}
cout << ans << '\n';
}
B. Generate Permutation
题意:给定两个指针l和r,初始时分别在数组首末。每次操作可以将指针往对应的方向移动1,或者在当前的下标上将初始值-1改为一个数组未出现最小的正数,或者将指针回溯到起始点。 问是否有一个permutation,不管用左指针构造还是右指针构造,他们回溯到起始点的操作数是相等的。
思路:偶数时无解。 奇数时把1放到数组最中间,然后维护两个指针,一个往右加数,一个往左加数,并在每次移动一个距离后将两个指针的值互换,这样保证左右两个指针在构造时,回溯次数相等。
比如 n = 5
-1 -1 1 -1 -1
-1 3 1 2 -1
5 3 1 2 4
左指针构造要回溯2次,右指针也是两次。
总结:先看测试示例,大概能看出来偶数长度无解。考虑奇数长度,为了让左右指针的回溯次数相等,肯定是中间有一个中位数,剩下的数往两边补,补的过程中要保证左右指针回溯次数相等。而为了保证增加数时,保证指针的回溯次数相等,那肯定是一次考虑增加两个数,如果只增加一个数,必然有一个指针不用回溯。 有了这个思路,直接拿n = 3和n = 5试一下,就知道如何写代码了。
void solve(){
int n;
cin >> n;
if (n % 2 == 0) {
cout << "-1\n";
}
else {
int m = n / 2;
int cnt = 1;
int i = m + 1, j = m - 1;
vector<int> ans(n);
ans[m] = 1;
while (j >= 0) {
if (j & 1) {
ans[i] = ++cnt;
ans[j] = ++cnt;
}
else {
ans[j] = ++cnt;
ans[i] = ++cnt;
}
i ++;
j --;
}
for (int i = 0; i < n; ++i) {
cout << ans[i] << " \n"[i == n - 1];
}
}
}
赛时很快A完2道题,然后开始坐牢,交互题是真不想做啊,看了一遍题目,就没继续了。
D题看错了,把数值范围看成了1~9(我寻思最大最小字典序数字都不超过10啊),然后调试了半个多小时,发现读错题了
直接关机睡觉。
哎 又要掉大分了
D. Longest Max Min Subsequence
题意:给定n个数,求最长的包含不相同元素的满足特定条件的子序列。特定条件:最长子序列中的奇数位*-1后,字典序最小
思路:维护每个数出现的次数一个一个ans数组,然后依次遍历n个数。当遍历到第i个数时,如果ans为空,则加入数到ans,并且标记该数已在ans中出现过,否则循环的考虑ans中的倒数第一个数和倒数第二个数,看当前数在替代某一个数的情况下,是否可以不改变最终ans的长度(通过查看后续数字中是否出现过ans中的这两个数来判定),以及是否会让字典序更小(通过判定奇数偶数位来决定是否当前数字更优,奇数位应该找大数,偶数位找小数),如果更优,则pop出对应的数,否则加当前数加入到数组末尾。
总结:赛时看错题了,以为只有19,于是维护了每个位置下一个19数字出现的次数,并判断当前位是奇数还是偶数位,然后在不改变最终子序列长度的情况下,选个最优的数,结果在调试的时候发现。。范围搞错了。
这个题目的类型,应该属于最长子序列的变种题。最长子序列的经典问题包括最长非递增,非递减,递增递减子序列,要用特有的算法去求解。
对于当前的题目,也可以归结为一种特别的算法,最长不相同元素子序列(好像直接set去重就行,太简单了),那就加个限制,最长递增不相同元素子序列(差不多可以认为是个模板题吧)。
reserve后代码评测使用内存从1400k降到了0,神奇。
void solve(){
int n;
cin >> n;
vector<int> a(n);
vector<int> ans;
ans.reserve(n);
for (auto& x : a) {
cin >> x;
}
int s = *max_element(a.begin(), a.end());
vector<int> cnt(s + 1);
vector<bool> has(s + 1);
for (const auto&x : a) {
cnt[x] ++;
}
auto better = [](int x, int y, bool odd) {
return odd ? x > y : x < y;
};
for (int i = 0; i < n; ++i) {
cnt[a[i]] --;
if (ans.empty()) {
ans.push_back(a[i]);
has[a[i]] = true;
continue;
}
if (has[a[i]]) {
continue;
}
while (!ans.empty()) {
int m = (int)ans.size();
if (cnt[ans[m - 1]] && better(a[i], ans[m - 1], m & 1)) {
has[ans[m - 1]] = false;
ans.pop_back();
}
else if (m > 1 && cnt[ans[m - 2]] && cnt[ans[m - 1]] && better(a[i], ans[m - 2], (m - 1) & 1)) {
has[ans[m - 2]] = false;
has[ans[m - 1]] = false;
ans.pop_back();
ans.pop_back();
}
else {
break;
}
}
ans.push_back(a[i]);
has[a[i]] = true;
}
cout << ans.size() << '\n';
for (int i = 0; i < ans.size(); ++i) {
cout << ans[i] << " \n"[i == ans.size() - 1];
}
}
标签:cnt,967,int,回溯,Codeforces,++,ans,Div,指针
From: https://www.cnblogs.com/yxcblogs/p/18371113