更好的阅读体验:
题目链接:
题意简述:
- 给出一个排列, 长度为
n
n
n 问是否能通过
n
−
1
n-1
n−1 次交换 (第
i
i
i 位有且仅可以与
i
+
1
i+1
i+1 位交换一次), 使这个排列递增. (必须交换
n
−
1
n-1
n−1 次)
思路:
- 仅能使用一次, 这意味着如果右边有一个数应在这个位置的左边, 那么通过这仅有一次机会
- 如果这一个机会被占用了, 那么这个数就不能归位了喵 …
做法:
- 所以, 应当使当前未归位最小数先归位, 否则左边的交换会阻止其到达, 右边的交换则对此无影响
- 定义一个变量
u
s
e
use
use 为使用过的右侧边界,若当前要归位的数在
u
s
e
use
use 左边,说明已经使用过, 则不可行
if(i<=use && date[i]!=i){
puts("-1");
return 0;
}
for(int i=1;i<=n;++i){
if(i<=use && date[i]!=i){
puts("-1");
return 0;
}
use=zb[i]-1;
for(int e=use;e>=i;--e){
swap(date[e],date[e+1]);
zb[date[e]]=e;
zb[date[e+1]]=e+1;
ans.push_back(e);
}
}
- 当结束循环后, 查看是否使用了
n
−
1
n-1
n−1 次.
if((int)ans.size()!=n-1){puts("-1");return 0;}
小贴士:
- 上述代码中使用了
z
b
zb
zb 数组来存储每个数的位置, 而
d
a
t
e
date
date 数组则是存每个位置是什么数, 记得两个都要更新喵
- 这里使用了
v
e
c
o
t
r
vecotr
vecotr 来存储答案, 蒟蒻认为这样简单一些
时间复杂度分析
- 由于
u
s
e
use
use 每个点只能有一次, 而
u
s
e
use
use 单调不降, 最多对
u
s
e
use
use 进行
n
n
n 次操作, 故时间复杂度为
O
(
n
)
O(n)
O(n)
代码:
#include <stdio.h>
#include <ctype.h>
#include <algorithm>
#include <vector>
#define lnt long long
using namespace std;
int xx;char ff,chh;inline int read(){
xx=ff=0;while(!isdigit(chh)){if(chh=='-'){ff=1;}chh=getchar();}
while(isdigit(chh)){xx=(xx<<1)+(xx<<3)+chh-'0';chh=getchar();}return ff? -xx: xx;
}
const int N=1e6;
int n,use,date[N],zb[N];
vector<int> ans;
int main(){
n=read();
for(int i=1;i<=n;++i){
date[i]=read();
zb[date[i]]=i;
}
for(int i=1;i<=n;++i){
if(i<=use && date[i]!=i){
puts("-1");
return 0;
}
use=zb[i]-1;
for(int e=use;e>=i;--e){
swap(date[e],date[e+1]);
zb[date[e]]=e;
zb[date[e+1]]=e+1;
ans.push_back(e);
}
}
if((int)ans.size()!=n-1){puts("-1");return 0;}
for(auto i: ans){printf("%d\n",i);}
return 0;
}