UVa 1611 Crane

本文提出了一种高效的排序方法,通过对冒泡排序的改进,利用特定条件下的两次交换操作,实现了更优的时间复杂度O(2n),而非传统的O(n^2)。通过分析不同情况下最优的交换策略,该算法能有效地减少不必要的交换次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4486


#include <cstdio>
#include <utility>
#include <vector>
using namespace std;
// place[i]代表i所在的位置
int place[10010];

// 现在的排列
int array[10010];

// 交换的过程
vector<pair<int,int> > my_swap;

void do_swap(int left, int right);

void print_place();

int n;

int main()
{
	int T;
	scanf("%d", &T);
	int count = 0;
	while(count < T)	
	{
		my_swap = vector<pair<int,int> >();
		scanf("%d", &n);
		for(int i = 1; i <= n; i++)
		{
			scanf("%d", &array[i]);
			place[array[i]] = i;
		}		

//		print_place();

		// 依次归位每个元素
		for(int i = 1; i <= n-1; i++)			
		{
			if(place[i] != i)
			{
				// 如果该元素距离其初始位置大于整个序列的一半
				// 以该元素为末尾,来将路程减半
				if(place[i]-i > (n-i+1)/2)
				{
					if((place[i]-i+1) % 2 == 0)
					{
						my_swap.push_back(pair<int,int>(i, place[i]));
//						printf("this here: (%d,%d)\n", i, place[i]);
						// 实行交换
						do_swap(i, place[i]);
					}	
					else		
					{
						my_swap.push_back(pair<int,int>(i+1, place[i]));
//						printf("this here: (%d,%d)\n", i+1, place[i]);
						// 实行交换
						do_swap(i+1, place[i]);
					}
				}


//				print_place();
				// 当该元素距离其初始位置小于整个序列一半
				// 将该元素移至正确位置
				int end = place[i] + place[i]-i-1;
				my_swap.push_back(pair<int,int>(i, end));
//				printf("this: (%d,%d)\n", i, end);
				do_swap(i, end);		
//				print_place();
			}	
		}
	
		// 输出
		printf("%d\n", my_swap.size());
		for(int i = 0; i < my_swap.size(); i++)
			printf("%d %d\n", my_swap[i].first, my_swap[i].second);
		count++;	
	}	
	return 0;
}

// 实行交换
// 交换array[left ... right]
void do_swap(int left, int right)
{
	int num = (right-left+1)/2;
	for(int i = 0; i < num; i++)
	{
		place[array[left+i]] = left+i+num;
		place[array[left+i+num]] = left+i;
		int t_num = array[left+i];	
		array[left+i] = array[left+i+num];
		array[left+i+num] = t_num;
	}	
}

void print_place()
{
	printf("now_array: ");
	for(int i = 1; i <= n; i++)
		printf("%d ", array[i]);
	printf("\nnow_place: ");
	for(int i = 1; i <= n; i++)
		printf("%d:%d ", i, place[i]);
	printf("\n");
}



感觉是道好题。

本质是将一堆数进行排序,可以用的操作只有交换两个相邻的序列。

最简单可以想到针对每个数,依次交换相邻位置,总可以让这个数到达正确的位置,此时相当于冒泡排序。

仔细想一下会发现,如果一个数的现在位置距离它的正确位置小于或等于序列的一半,那么直接以该数为第二个序列头,以正确位置上的数为第一个序列头,实行一次交换即可。

如果一个数的现在位置距离它的正确位置大于序列的一半,那么可以做的是尽量让该数先靠近正确位置,进行一次交换,可以使该距离至少折半,然后再进行一次上述交换即可。

这样会发现,交换的次数为O(2n)级别。而不是O(n^2).

不管是冒泡排序还是上述最多2次交换排序,重要的想法是:考虑每个数,将最大/最小的数移到正确位置后,再考虑剩下的序列。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值