题目:
题目描述
有N(1≤N≤1000)头奶牛,它们都被标上一个优先等级编号:1,2或3。用来表示它们喝水时的优先次序,编号为l的最优先,编号为2的其次,编号为3的最后。每天奶牛开始时排成一行,但总是很乱,需要你把它们重新排成编号为1的奶牛在最前面,编号为2的其次,编号为3的奶牛在最后。你能计算出最少需要多少的交换次序来完成这次重排吗?
输入
第1行:1个整数N;
第2至N+I行:第i+l行有一个整数表示开始队列中第i头奶牛的编号。
输出
1行,只一个整数,表示最少需要交换次数。
样例输入
9
2 2 1 3 3 3 2 3 1
样例输出
4
思路:在读入的时候记录一下1 2 3三个数的数目,然后就可以确定排完序之后他们的界限,然后从1的区域开始扫,扫到2就优先去2的区域找,找不到就去3的区域找,扫到3同理,这样就能保证每一次交换都是最优的,将2区域扫完之后也就完成了所有的交换。如果在1的区扫描到4则同样优先到4的区寻找1,找不到的话从“1的区和4的区”的之外所有区进行寻找,最后交换。
import java.util.*;
/**
* Created by Daxin on 2017/8/19.
* <p/>
* 奶牛排队饮水问题
* 输入:n牛的数目,然后n个整数表示牛的序号
* 输出:输出交换最少次数
* <p/>
* 例如一个测试用例:9<br>
* 2,2,1,3,3,3,2,3,1<br>
* 输出:4
*
*在线题目地址:http://www.hustoj.com/oj/problem.php?id=1056
*
*
*/
public class CattleSortWater {
public static void main(String[] args) {
// int[] nums = {2, 2, 1, 3, 3, 3, 2, 3, 1};
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = cin.nextInt();
}
System.out.println(solve(nums));
// System.out.println(Arrays.toString(nums));
}
public static int solve(int[] nums) {
int result = 0;
Map<Integer, Integer> wcount = new TreeMap<>();
Map<Integer, Integer> range = new TreeMap<>();
for (int num : nums) {
Integer tmp = wcount.get(num);
wcount.put(num, tmp == null ? 1 : tmp + 1);
}
Set<Integer> set = wcount.keySet();
int pre = 0;
for (int i : set) {
int tmp = wcount.get(i) + pre;
range.put(i, tmp);
pre = tmp;
}
for (int num : set) {
int times = wcount.get(num);
int ran = range.get(num);
for (int i = ran - 1, t = times; t > 0; t--, i--) {
if (nums[i] != num) {
int r = get(nums, range.get(nums[i]), wcount.get(nums[i]), num);
if (r != -1) { //在nums[i] 区间中寻找到了num
int tmp = nums[r];
nums[r] = nums[i];
nums[i] = tmp;
result++;
} else {// 在希望区间没有找到,进行余下遇见全扫描
int rr = getRange(nums, ran, num,range.get(nums[i])-wcount.get(nums[i]),range.get(nums[i]));
if (rr != -1) {
int tmp = nums[rr];
nums[rr] = nums[i];
nums[i] = tmp;
result++;
}
}
}
}
}
return result;
}
/**
*
* @param nums 数组
* @param range 结束的下标
* @param times 出现的次数,range-times就是起始下标
* @param expect 希望查找的希望值
* @return
*/
public static int get(int[] nums, int range, int times, int expect) {
for (int i = range - 1, t = times; t > 0; t--, i--) {
if (nums[i] == expect) {
return i;
}
}
return -1;
}
/**
*
* @param nums 数组
* @param start 查找的起始位置,结束位置是数组的结束
* @param expect 期望寻找到的值
* @param exceptStart 排除区域的起始下标
* @param exceptEnd 排除区域的结束下标
* @return 返回下标
*/
public static int getRange(int[] nums, int start, int expect,int exceptStart,int exceptEnd) {
for (; start < nums.length; start++) {
if(start>=exceptStart&&start<exceptEnd)
continue;
if (nums[start] == expect) {
return start;
}
}
return -1;
}
}
在线题目地址:http://www.hustoj.com/oj/problem.php?id=1056