YTU 1423 / 洛谷 1459 2.1.3 Sorting a Three-Valued Sequence 三值的排序

文章讨论了如何通过最少的交换次数将只包含1,2,3的数字序列排成升序,提出了一种基于优先级的策略,通过遍历并交换适当位置的数字来达到目标。
摘要由CSDN通过智能技术生成

题目描述

排序是一种很频繁的计算任务。现在考虑最多只有三值的排序问题。一个实际的例子是,当我们给某项竞赛的优胜者按金银铜牌序的时候。 在这个任务中可能的值只有三种 111,222 和 333。我们用交换的方法把他排成升序的。 写一个程序计算出,给定的一个由 1,2,31,2,31,2,3 组成的数字序列,排成升序所需的最少交换次数。

输入

第一行输入 NNN(1≤N≤10001 \le N \le 10001≤N≤1000)。
第 222 到 N+1N+1N+1 行,每行一个数字(111、222 或 333),共 NNN 行。

输出

共一行,一个数字。表示排成升序所需的最少交换次数。

输入输出样例

样例输入 #1

复制

9
2
2
1
3
3
3
2
3
1
样例输出 #1

复制

4

分析:

        这道题难点有一个,就是如何换位,和谁优先换位 。那么在做题之前呢我们先要了解,因为就只有三个数,是不是我们把1和2都排序好,是不是就完成任务了呢。所以我们只需要对1和2进行处理就行。

        对于和谁换位的问题,我们要优先和在其位置上的数换。举个例子,对于下面的数列,有三个1,两个2,三个三,1 3 2 1 2 3 1 3,分别记为a[1],a[2]...a[8],你要怎么做呢,我们要换a[2]数字“3”,那么是和a[4]换呢还是和a[7]换呢,答案肯定是和a[7]换对吧,因为这样的话我们在后期就会比和a[4]换少一步,因为我们不用再去处理那个3了,他在那待着就行了对吧。

        好,道理我们都懂,可是要怎么实现呢,哎我们可以分情况讨论:我们先把1,2的数量还有所有总数记下来,记作sum1,sum2 ,n,那么1到sum1就是所有的1该待的地方,sum1+1到sum1+sum2就是2改待的地方,同理sum1+sum2+1到n就是3该待的地方。

        当我们在遍历1的地盘1到sum1:

如果在这个点的是2,我们是不是要优先从二的地盘找2来换啊,好那么就在sum1+1到n这里找2,有同学可能问了,为什么要到n呢,为什么不是sum1+sum2呢,因为有可能2的地盘里没有2,所以我们还要往后再找这样才能保证一定可以找到2;

同理,如果这个点是3呢,那我们就从n开始倒着找,因为3的地盘肯定靠着n往前的。

        当我们在遍历2的地盘sum1+1到sum1+sum2,注意这个时候1肯定是都排好了,往后的数要么是2,要么是3,那就不用再分情况了呗:

如果这个点是3,那我们还是从n开始倒着找,因为3的地盘肯定靠着n往前的。

OK!这样我们就完成了我们大体思想,下面上代码!

代码:

 

#include<iostream>
using namespace std;
int n;
int arr[10005];
int num1, num2, ans, sum1, sum2;//num1,sum1是1的个数,num2,sum2是2的个数
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> arr[i];
		if (arr[i] == 1)//记录
			num1++;
		if (arr[i] == 2)
			num2++;
	}
	sum1 = num1, sum2 = num2;
	for (int i = 1; i <= n; i++)
	{
		if (num1 == 0 && num2 == 0)//如果1和2的数量都是0了,那么就停止遍历就可以了
			break;
		if (num1 != 0 && arr[i] != 1)//如果1还有没排好的,遍历到的点还不等于1,那么就该换位了
		{
			if (arr[i] == 2)//如果这个点是2的话
			{
				for (int j = sum1 + 1; j <= n; j++)//从sum1+1开始往后遍历找1,因为在sum1之前的1都不用动他,早晚sum1之前都得是1
				{
					if (arr[j] == 1)//找到了
					{
						int t;//换!
						t = arr[i];
						arr[i] = arr[j];
						arr[j] = t;
						ans++;//换的次数加一
						break;//找到一个就行了,记得要break
					}
				}
			}
			else if (arr[i] == 3)//如果这个点是3的话
			{
				for (int j = n; j >= 0; j--)//下面的步骤都同理
				{
					if (arr[j] == 1)
					{
						int t;
						t = arr[i];
						arr[i] = arr[j];
						arr[j] = t;
						ans++;
						break;
					}
				}
			}
		}
		if (num1 == 0 && arr[i] != 2)//如果1已经没了,说明该排序2了
		{
			for (int j =n; j >=num1+num2+1; j--)//2的话就不用和1那样麻烦了,因为后边的点不是2就是3,不用在分类了,如果是3的话,那么我们从最后边到sum1+sum2+1开始遍历
			{
				if (arr[j] == 2)//找到了
				{
					int t;//下面都同理
					t = arr[i];
					arr[i] = arr[j];
					arr[j] = t;
					ans++;
					break;
				}
			}
		}
		if (num1 != 0 && arr[i] == 1)//最后看一看正在遍历的这个点是不是1,是的话num1--
			num1--;//为什么要写到最后来判断呢,这样的话可以不用分类,就是不用去区分这个点是不是需要换位的点
		if (num1 == 0 && arr[i] == 2)//最后看一看正在遍历的这个点是不是2,是的话num2--
			num2--;
	}
	cout << ans;
	return 0;
}

  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值