7-107 与零交换

题目如下:

将 { 0, 1, 2, ..., N-1 } 的任意一个排列进行排序并不困难,这里加一点难度,要求你只能通过一系列的 Swap(0, *) —— 即将一个数字与 0 交换 —— 的操作,将初始序列增序排列。例如对于初始序列 { 4, 0, 2, 1, 3 },我们可以通过下列操作完成排序:

  • Swap(0, 1) ⟹ { 4, 1, 2, 0, 3 }
  • Swap(0, 3) ⟹ { 4, 1, 2, 3, 0 }
  • Swap(0, 4) ⟹ { 0, 1, 2, 3, 4 }

本题要求你找出将前 N 个非负整数的给定排列进行增序排序所需要的最少的与 0 交换的次数。

输入格式:

输入在第一行给出正整数 N (≤10​5​​);随后一行给出{ 0, 1, 2, ..., N-1 } 的一个排列。数字间以空格分隔。

输出格式:

在一行中输出将给定序列进行增序排序所需要的最少的与 0 交换的次数。

输入样例:

10
3 5 7 2 6 4 9 0 8 1

输出样例:

9

根据题意以该位置的数值进行交换,时间复杂度为O(n^2),会超时,代码1如下:

#if 1
#include <iostream> 

using namespace std;
const int maxn=100005;
int c_time(int *a,int n,int t)
{
	int cnt=0;
	while(1)
	{
		if(t!=0)
		{
			for(int i=0;i<n;i++)
			{
				if(a[i]==t)
				{
					swap(a[t],a[i]);
					t=i;
					cnt++;
					break;
				}
			}
		}
		else
		{
			int t_num=0,j=0;
			for(int i=0;i<n;i++)
			{
				if(a[i]!=i)
				{
  				    swap(a[t],a[i]);
  				    t=i;
  				    cnt++;
  				    break;
				}
			}
			if(t==0) return cnt;
		}
	}
}
int a[maxn];
int main()
{
	int n,t;	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]==0) t=i;
	}
	cout<<c_time(a,n,t)<<endl;
	return 0;
}
#endif

根据数值下标去交换,时间复杂度为O(n)代码2如下:

#if 1
#include <iostream>
using namespace std;
const int maxn=1e5+5;
int circle(int *a,bool *b,int i)
{
	int t=0,flag=0;
	while(!b[i])
	{
		if(i==0)	flag=1;
		t++;
		b[i]=true;
		i=a[i];
	}
	if(flag)	return t-1;
	else	return t+1;
}
int a[maxn];
bool b[maxn];
int main()
{
	int n;	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",a+i);
		if(i==a[i])	b[i]=true;
	}
	int sum=0;
	for(int i=0;i<n;i++)
	{
		if(!b[i])	sum+=circle(a,b,i);
	}
	cout<<sum<<endl;
	return 0;
}
#endif

代码1和代码2的思路互为相反。本质上,是有向环,因此,环的开头是谁并不重要,但只能有一个开头,环于环时互不相交的。另外,有向环中包含0时,实际次数为t-1(0在其中,正常交换,环的节点数t减1);不包含0时,实际次数为t+1(在正常交换的基础上(t-1次),0要进去,也要出来,所以变成(t+1))。以上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值