zoukankan      html  css  js  c++  java
  • PAT Advanced 1067 Sort with Swap(0,*) (25) [贪⼼算法]

    题目

    Given any permutation of the numbers {0, 1, 2,…, N-1}, it is easy to sort them in increasing order. But what if Swap(0, *) is the ONLY operation that is allowed to use? For example, to sort {4, 0, 2, 1, 3} we may apply the swap operations in the following way:
    Swap(0, 1) => {4, 1, 2, 0, 3}
    Swap(0, 3) => {4, 1, 2, 3, 0}
    Swap(0, 4) => {0, 1, 2, 3, 4}
    Now you are asked to find the minimum number of swaps need to sort the given permutation of the first N nonnegative integers.
    Input Specification:
    Each input file contains one test case, which gives a positive N (<=105) followed by a permutation sequence of {0, 1, …, N-1}. All the numbers in a line are separated by a space.
    Output Specification:
    For each case, simply print in a line the minimum number of swaps need to sort the given permutation.
    Sample Input:
    10 3 5 7 2 6 4 9 0 8 1
    Sample Output:
    9

    题目分析

    已知N个任意排列的数字,要求最少交换次数对其排序(排序过程只能使用0与其他数字交换位置

    解题思路

    1. 贪心算法:如果当前数字0在i号位置,找到数字i当前所处位置,将数字0和i交换

    证明:
    由于0必须参加交换操作,因此通过该策略,每步总是可以将一个非零的数回归本位。如果用0与其他不是该位置编号的数进行交换,显然会产生一个无效操作,因为后续操作中还是需要将刚才交换的数换回本位,因此该策略能将无效操作次数(与0交换的数没有回归本位的次数)降到最小,于是最优。
    第一步: 0在7号位,因此将0与7交换,得到序列3502649781
    第二步: 0在2号位,因此将0与2交换,得到序列3520649781
    第三步: 0在3号位,因此将0与3交交换,得到序列0523649781
    第四步: 0在0号位,因此将0与一个还未在本位的数字5交换,得到序列得到序列5023649781
    第五步: 0在1号位,因此将0与1交换,得到序列5123649780
    第六步: 0在9号位,因此将0与9交换,得到序列5123640789
    第七步: 0在6号位,因此将0与6交换,得到序列5123046789
    第八步: 0在4号位,因此将0与4交换,得到序列5123406789
    第九步: 0在5号位,因此将0与5交换,得到序列0123456789
    此时序列有序,总交换次数为9次。
    //style="background-color: #FF8C00;"

    1. 定义数组int pos[N],记录数字所在位置(如:pos[i],表示数字i在pos[i]的位置)
    2. 哨兵为数字0,pos[0]=0,不一定排序已完成
      • pos[0]==0,但是排序未完成,将0需要与不在本位的数字交换(因为如果0与已回到本位的数字交换会导致交换次数变多),定义数字k初始化为1,记录当前不在本位的最小数字(避免每次都循环遍历数组,查找未回到本位的数字(复杂度为O(n^2)有两个点会超时))
      • pos[0]==0,已完成排序(如何检测已完成排序?--定义变量left记录除0以外不在自己本位数字的个数,left=0表示排序完成)

    注意点

    1. 当0回到本位时,并不一定保证数字都已回到本位。此时需要找到一个未在本位的数字与0进行交换(如果选择已在本位的数字与0交换会导致交换次数增多)

    Code

    Code 01

    #include <iostream>
    #include <string>
    using namespace std;
    int main(int argc, char * argv[]) {
    	int N,m,cnt=0;//cnt--总交换次数
    	scanf("%d",&N);
    	int pos[N]= {0};// pos[i]--数字i的位置为pos[i]
    	int left=N-1; // 除0以外不在本位的数字
    	for(int i=0; i<N; i++) {
    		scanf("%d", &m);
    		pos[m]=i; // 数字m在位置i
    		if(m==i&&m!=0)left--; // 除0以外已在本位,left--
    	}
    	int k=1; //当前最小不在本位的数字,避免0在本位但排序未完成时,每次都需要遍历数组找未在本位的数字(复杂度O(n^2)会超时)
    	while(left>0) {
    		if(pos[0]==0) { //如果排序未完成,0已回到本位,将0与最小不在本位的k进行互换
    			while(k<N) {
    				if(pos[k]!=k) { //如果k不在本位 
    					swap(pos[0],pos[k]);
    					cnt++; //无需left++,因为k本来就不再本位,0在本位但是left记录的是非0不在本位的数字个数 
    					break;
    				}
    				k++; //如果k在本位,向前找最小不在本位的k 
    			}
    		}
    		while(pos[0]!=0) { //0不在本位 
    			swap(pos[0],pos[pos[0]]);
    			left--; //有一个数字回归本位 
    			cnt++;
    		}
    	}
    	printf("%d",cnt);
    	return 0;
    }
    

    Code 02

    #include <iostream>
    #include <string>
    using namespace std;
    int main(int argc, char * argv[]) {
    	int N,m,cnt=0;
    	scanf("%d",&N);
    	int pos[N]= {0};
    	for(int i=0; i<N; i++) {
    		scanf("%d", &m);
    		pos[m]=i;
    	}
    	for(int i=0; i<N; i++) { //个人理解,这里处理不是很好,因为已遍历过的i不能保证其已回到本位
    		if(pos[i]!=i) {
    			while(pos[0]!=0) {
    				swap(pos[0],pos[pos[0]]);
    				cnt++;
    			}
    			if(pos[i]!=i) {
    				swap(pos[0],pos[i]);
    				cnt++;
    			}
    		}
    	}
    	printf("%d",cnt);
    	return 0;
    }
    
  • 相关阅读:
    电子产品使用感受之—我的iPad Pro坏了。。。
    我最近读了什么书?(随时更新)
    我了解到的新知识之--GDPR
    我了解到的新知识之—MPLS
    我了解到的新知识之—Apple Captive Portal 网页认证登陆公共Wifi
    电子产品使用感受之--无线充电器到底是不是鸡肋?
    大家春节好,过节了也不要忘记来听听我的播客节目哦!
    Python学习笔记之--我又开始学习Python了(随时更新)
    电子产品使用感受之--Mac Mini 买了之后有什么用?-- 开发啊!
    第一次个人编程作业
  • 原文地址:https://www.cnblogs.com/houzm/p/12244928.html
Copyright © 2011-2022 走看看