题目描述
难度分:2600
输入n(1≤n≤2×105)和n个数对(ai,bi),保证这2n个数在[1,2n]中且互不相同。
有n张牌,第i张牌的正面是ai,反面是bi。
你可以随意排列这些牌,也可以翻转牌(交换ai和bi)。
要使a是递增的,b是递减的,最少要翻多少张牌?如果无法做到,输出−1。
输入样例1
5
3 10
6 4
1 9
5 8
2 7
输出样例1
2
输入样例2
2
1 2
3 4
输出样例2
-1
输入样例3
3
1 2
3 6
4 5
输出样例3
-1
算法
构造
完全不会做,参考灵佬的构造方法。
举例说明
从写有1的牌开始。这张牌要么在最左边(1在上),要么在最右边(1在下)。不妨设其在最左边。
假设1反面的数字是8。
现在牌是这样放的(下划线表示空位):
1 _ _ _ _
8 _ _ _ _
比8大的那些数应该在哪?假如最大的数是10,那么10只能在最右边且朝上。9也同理。假设10的反面的数字是3,9的反面的数字是4。现在牌是这样放的:
1 _ _ 9 10
8 _ _ 4 3
比3小的数(2)应该在哪?只能在1右边且朝上。假设其反面为7。现在牌是这样放的:
1 2 _ 9 10
8 7 _ 4 3
不断重复上述过程,直到没有必须放在左/右的牌。
上述过程中,如果放了x张牌,其中翻转了k张牌,那么不翻转这k张牌,改为翻转另外x−k张牌,也可以满足题目要求,所以每一轮把min(k,x−k)累加在答案上。
如果发现没有必须放在左/右的牌,那么剩余问题变成一个规模更小的子问题解决。最后判断两个数组是否有序即可(说得容易,其实代码也不是很好写,还需要好好学习代码)。
复杂度分析
时间复杂度
整个过程就相当于把2n个数填在O(n)级别的位置上,因此时间复杂度为O(n)。
空间复杂度
参考代码,其中所有的辅助数组都是线性空间,因此额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
int n;
void solve() {
cin >> n;
int ans = 0;
vector<int> to(2 * n + 1), is_back(2 * n + 1);
vector<PII> ps(n);
for(int i = 0; i < n; ++i) {
cin >> ps[i].first >> ps[i].second;
is_back[ps[i].second] = 1;
to[ps[i].first] = ps[i].second;
to[ps[i].second] = ps[i].first;
}
vector<int> a(n), b(n);
int al = 0, ar = n - 1, bl = 0, br = n - 1;
vector<bool> vis(2 * n + 2);
int mn = 1, mx = 2 * n;
while(true) {
while(vis[mn]) ++mn;
if(mn > 2 * n) break;
vector<int> cnt(2, 0);
int t = mn;
while(true) {
int last = 0;
for(; mn <= t; ++mn) {
if(vis[mn]) continue;
cnt[is_back[mn]]++;
vis[mn] = true;
a[al++] = mn;
last = to[mn];
vis[last] = true;
b[bl++] = last;
}
if(last == 0) break;
t = last;
last = 0;
for(; mx >= t; --mx) {
if(vis[mx]) continue;
cnt[is_back[mx]]++;
vis[mx] = true;
a[ar--] = mx;
last = to[mx];
vis[last] = true;
b[br--] = last;
}
if(last == 0) break;
t = last;
}
ans += min(cnt[0], cnt[1]);
}
reverse(b.begin(), b.end());
if(!is_sorted(a.begin(), a.end()) || !is_sorted(b.begin(), b.end())) {
cout << -1 << endl;
return;
}
cout << ans << endl;
}
int main() {
solve();
return 0;
}