求将n个互异的正整数变成上升序列时的最少交换次数

问题:有n个互异的正整数,定义将任意两个数的位置进行交换称为一次操作。求将n个数变成上升序列时的最少操作数。

 

       当n=11时,有序列8 3 9 1 5 6 11 4 7 10 2,现在要把它通过交换变成上升序列1 2 3 4 5 6 7 8 9 10 11。可以看出,'8'应该放在第8个位置,而在第8个位置的'4'应该被交换到第4个位置,而在第4个位置的‘1’应该被交换到第1个位置,而此时在第1个位置的正是一开始就拿来交换的数字'8',也就是说,数字'8' '4' '1'形成了一个循环圈,交换2次(8-4,4-1),则数字‘1’‘4’‘8’都能到达最终位置了,在之后的交换中不会在用到这3个数字。同理,对序列的其他数字也如此处理,可以得出其余4个循环圈,(3 9 7 11 2),(5),(6),(10)。那么,最少交换次数 = (3-1)+(5-1) +(1-1) +(1-1)+(1-1) = 6

       假设n个数分成k个“循环圈”,每个“循环圈”的个数分别是a1,a2,……,ak,那么最少交换次数 = (a1-1)+(a2-1)+……+(ak-1) = (a1+a2+……+ak) - 1 * k = n - k

因此,最少交换次数 = 序列个数 - “循环圈”个数

 

本文参考http://blog.csdn.net/wangxugangzy05/article/details/42454111

 

参考程序:

 

#include<stdio.h>
#include<string.h>
#define maxn 10001

int a[maxn];
int n;

void init()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
}

void jishu_sort(int a[], int n, int sa[], int rank[])
{
	int count[maxn];
	memset(count, 0, sizeof(count));
	int i;
	for(i = 1; i <= n; i++)
		count[a[i]]++;

	for(i = 1; i < maxn; i++)
		count[i] += count[i-1];

	for(i = 1; i <= n; i++)
	{
		rank[i] = count[a[i]]--;
	}

	for(i = 1; i <= n; i++)
		sa[rank[i]] = a[i];
}

void solve()
{
	int sa[maxn]; //sa[i]表示排第i的是谁
	int rank[maxn]; //rank[i]表示第i个排第几
	memset(sa, 0, sizeof(sa));
	memset(rank, 0, sizeof(rank));

	jishu_sort(a, n, sa, rank); //用计数排序对a数组进行升序排序

	int sign[maxn]; //sign[i]表示第i个数属于第几个“循环圈”
	memset(sign, 0, sizeof(sign));	
	int circle = 0;
	for(int i = 1; i <= n; i++)
		if(!sign[i])
		{
			sign[i] = ++circle;
			int x = sa[rank[i]];
			while(!sign[x])	//找出这个循环圈的其他数字,并写上编号
			{
				sign[x] = circle; //写号
				x = sa[rank[x]];  //找下一个
			}
		}

	//output answer
	printf("%d\n", n - circle);
}

int main()
{
	init();
	solve();
	return 0;
}

 

 

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值