NEU1511 Caoshen like math(使序列有序的最小交換次數)
題目描述
Worfzyq likes Permutation problems.Caoshen and Mengjuju are expert at
these problems . They have n cards,and all of the numbers vi on these
cards are different . Because Caoshen doesn’tlike disordered permutations,he wants to change the permutation into
non-descendingpermutation.He defines the operations:every time you can choose two
digits casually,andexchange the positions of them.Caoshen is lazy,he wants to know at
least how many operations heneeds to change the permutation into non-descending one?
輸入
There are multiple test cases. Each case contains a positive integer
n,incicate the number of cards(n<=1000000) . Followed by n positive
numbers integers v1,v2,…,vn (1≤vi≤n)—the value of each card.
輸出
Print the minmum operations in a signal line for each test case.
樣例輸入
5
1 3 2 5 4
樣例輸出
2
提示
first simple you need change (v2,v3) (v4,v5) and the permutation
change to 1 2 3 4 5 . so the minnum step is 2
思路
題意很簡單,給出一個序列,你可以交換任意兩個數字,問最少交換多少次可以使序列有序
不過可能有些難理解,我用通俗的方法說一說,我們可以利用圖論的方式來理解這個問題。
首先利用一個數組把序列裡面的數的原來位置利用一個數組來記錄一下,然後給原序列按照升序排序,我們知道了最終的結果,現在就是要如何從原來的位置,到達最終的位置的問題。
以樣例來說明:
下標 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
排序前 | 1 | 3 | 2 | 5 | 4 |
排序後 | 1 | 2 | 3 | 4 | 5 |
那麼我們看需要怎麼換才能到達最終的位置,首先1這個元素不用換,因為它排序後還是在1這個位置,所以就給1自己連一條邊,形成一個自環;元素3原來在2這個位置,但是排好序後它需要去3這個位置,而3這個位置的元素是2,那麼這兩個元素就需要交換,那麼就給元素2和元素3之間連一條邊,以此類推,連出所有的邊如圖:
1這個元素形成一個自環,2和3形成一個環,4和5形成一個環。我們知道,在一個環內,想把所有的元素都移動到最終的位置所需要的次數是環內的元素個數-1,那麼我們有3個環,就需要連2次就夠了。
那麼我們得出一個結論,只需要求出環的個數,然後用總的元素數量減去環的數量就是最後的答案,也就是很多部落格中的迴圈節
知道了原理,程式碼就很好寫了,實現可以有很多種方式~
程式碼
#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <set>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
const int N=1000000+20;
int a[N],vis[N],m[N];
int main()
{
int n,x;
while(~scanf("%d",&n))
{
mem(a,0);
mem(vis,0);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
m[a[i]]=i;
}
sort(a+1,a+n+1);
int ans=0;
for(int i=1; i<=n; i++)
{
if(!vis[i])ans++;;
int j=i;
while(!vis[j])
{
vis[j]=1;
j=m[a[j]];
}
}
printf("%d\n",n-ans);
}
return 0;
}