UVA 10570 Meeting with Aliens 外星人聚会

题意:给你一个排列,每次可以交换两个整数(不一定要相邻),求最少交换次数把排列变成一个1~n的环形排列。(正反都算)

其实就是找环了,对于一个链状序列,最小交换次数等于不在对应位置的数字个数减去环的个数。

至于证明这里讲的比较详细:http://www.dewen.io/q/7967#ans16319

所以只要枚举一下环的起点就好了,dfs找环就行了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+233;
#define bug(x) cout<<#x<<'='<<x<<endl;
int a[maxn];

bool vis[maxn];

int cnt,s;
void dfs(int u)
{
    if(vis[u]) return;
    cnt++;
    vis[u] = true;
    dfs(a[u+s]);
}
int n;

void dfs2(int u)
{
    if(vis[n-1-u]) return;
    cnt++;
    vis[n-1-u] = true;
    dfs2(a[n-1-u+s]);
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(~scanf("%d",&n)&&n){
        for(int i = 0; i < n; i++){
             scanf("%d",a+i); a[i+n] = --a[i];
        }

        int ans = INT_MAX;
        for( s = 0; s < n; s++){
            int cur = 0;
            memset(vis,0,sizeof(vis));
            for(int i = 0; i < n; i++)if(!vis[i]) {
                if(a[i+s] != i) {
                    vis[i] = true; cnt = 0;
                    dfs(a[i+s]);
                    cur += cnt;
                }else vis[i] = true;
            }
            ans = min(ans,cur);
        }
        reverse(a,a+2*n);
        for( s = 0; s < n; s++){
            int cur = 0;
            memset(vis,0,sizeof(vis));
            for(int i = 0; i < n; i++)if(!vis[i]) {
                if(a[i+s] != i) {
                    vis[i] = true; cnt = 0;
                    dfs(a[i+s]);
                    cur += cnt;
                }else vis[i] = true;
            }
            ans = min(ans,cur);
        }

        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2015-08-10 00:57  陈瑞宇  阅读(589)  评论(0编辑  收藏  举报