CF28B pSort 题解
CF28B pSort
题意
给定一个含有\(n\)个元素的数列,第\(i\)号元素开始时数值为\(i\),元素\(i\)可以与距离为\(d[i]\)的元素进行交换。再给定一个\(1-n\)的全排列,问初始的数列可否交换成给定的样式。
输入
第一行一个整数\(n\),第二行\(n\)个互不相同的整数表示目标数列,第三行\(n\)个整数表示\(d[i]\)。
输出
如果能交换到给定样式,输出"YES",否则输出"NO"。
数据范围
\(1 <= n <= 100\)
题解
<1>.首先看到对于第\(i\)个数,是可以跟\(i-d[i]\)和\(i d[i]\)交换位置的。考虑对于一个序列\(a_1,a_2,a_3...a_n\),任意打乱这个它的顺序使它成为\(b_1,b_2,b_3...b_n\),通过若干次交换相邻的数是可以使\(a[\ ]\)变为\(b[\ ]\)的。
证明:类似冒泡排序,定义每一个数\(a_i\)的优先级是 \(它在a[\ ]中的位置与它在b[\ ]中的位置\ 的距离\),那么通过冒泡排序,是可以在这种优先级下使\(a[\ ]\)变有序的,也就是让\(a[\ ]\)变为\(b[\ ]\)。
<2>:那么考虑在原数列中,如果第\(i\)个数可以与\(j\)交换位置,且\(j\)可以与\(k\)交换位置,那么\(i,j,k\)就可以交换为它们三个数的任意排列(1.中的结论),当然这是对任意多个数都成立的。
所以我们把可以互相交换(间接或直接)的数的下标看成一个集合,记为\(U\);那么对于\(U\)中的每一个元素\(i\),如果\(a[i](原始序列)\)在目标序列的位置\(v\)也在\(U\)中,那原序列的第\(i\)位就是符合条件的
<3>如果所有的\(a[i](原始序列)\)都是符合要求的,就说明所有的数字都可以换到目标序列上的位置,输出“YES”;只要有一个数字不符合要求,那么原序列就不能变为目标序列,输出"NO"
那么事实上
设原序列为\(a[\ ]\),目标序列为\(target[\ ]\);将每一个\(a[i]\)看作节点,向\(a[i d[i]]\)和\(a[i-d[i]]\)连边,只需要判断\(a[i]\)和\(target[i]\)是否联通即可
可以用并查集,也可以用floyd
代码
#include<bits/stdc .h>using namespace std;const int maxn = 105;int n, a[maxn], target[maxn], d[maxn];int father[maxn];int get_fa(int x) {if(x == father[x]) return x;return father[x] = get_fa(father[x]);}inline void merge(int x, int y) {int fa_x = get_fa(x);int fa_y = get_fa(y);if(fa_x == fa_y) return ;father[fa_y] = fa_x;}int main(){ ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n; for(int i = 1; i <= n; i) { cin >> target[i]; } for(int i = 1; i <= n; i) { cin >> d[i]; } for(int i = 1; i <= n; i) { a[i] = father[i] = i; } /*思路:判断a[i]与target[i]是否连通 */ for(int i = 1; i <= n; i) { int p1 = i - d[i]; int p2 = i d[i]; if(p1 >= 1) { merge(a[i], a[p1]); } if(p2 <= n) { merge(a[i], a[p2]); } } for(int i = 1; i <= n; i) { int fa1 = get_fa(a[i]); int fa2 = get_fa(target[i]); if(fa1 != fa2) { cout << "NO\n"; exit(0); } } cout << "YES"; return 0;}
