Java藍橋杯歷屆試題 小朋友排隊--運行超時,內存超大

問題描述
  n 個小朋友站成一排。現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。

  每個小朋友都有一個不高興的程度。開始的時候,所有小朋友的不高興程度都是0。

  如果某個小朋友第一次被要求交換,則他的不高興程度增加1,如果第二次要求他交換,則他的不高興程度增加2(即不高興程度爲3),依次類推。當要求某個小朋友第k次交換時,他的不高興程度增加k。

  請問,要讓所有小朋友按從低到高排隊,他們的不高興程度之和最小是多少。

  如果有兩個小朋友身高一樣,則他們誰站在誰前面是沒有關係的。
輸入格式
  輸入的第一行包含一個整數n,表示小朋友的個數。
  第二行包含 n 個整數 H1 H2 … Hn,分別表示每個小朋友的身高。
輸出格式
  輸出一行,包含一個整數,表示小朋友的不高興程度和的最小值。
樣例輸入
3
3 2 1
樣例輸出
9
樣例說明
  首先交換身高爲3和2的小朋友,再交換身高爲3和1的小朋友,再交換身高爲2和1的小朋友,每個小朋友的不高興程度都是3,總和爲9。
數據規模和約定
  對於10%的數據, 1<=n<=10;
  對於30%的數據, 1<=n<=1000;
  對於50%的數據, 1<=n<=10000;

  對於100%的數據,1<=n<=100000,0<=Hi<=1000000。

題目分析:其實就是算一個數的左邊有多少個大於它,右邊有多少個小於它,因爲只有逆序的兩個纔要交換,而且交換的時候兩個人的不開心程度都要加,求逆序對樹狀數組和歸併排序都可以,本人覺得歸併排序更好寫,用cnt記錄每個數字屬於的逆序對的個數,這個數有可能是比前面的小的也有可能是比後面大的,都要算進來,然後等差數列求和算這個人的總的不開心程度,然後把所有人的不開心程度累加即可,注意會爆int,歸併的時候要注意兩點,一是排序時下標變了,開始讀入要記錄下標,二是合併的兩個序列必然自身都是有序的,先求後面某個數的前面有多少個數比它大,計算時若d[i].num > d[j].num,那麼由其自身的有序性可知從第i個到第mid個元素都是大於d[j].num的,所以個數直接就是mid - i + 1,求前面某個數的後面比它小的個數同理。一開始寫的歸併得了70分,最後三組數據都超時了,原因是我在計算第一種情況的時候直接用循環計算第二種情況了,即因爲第i個到第mid個元素都是大於d[j].num的,就直接讓第i個到第mid個元素的cnt都加1,這樣在最壞的時候時間複雜度退化到n^2,所以是不可取的,正確的姿勢是,算第二種情況時倒着來一遍,原理同第一種

/**
 * 
 */
package 歷屆試題;

import java.util.Scanner;

/**
 * @author Administrator
 *
 */
public class 歷屆試題小朋友排隊 {

	/**
	 * @param args
	 *            問題描述 n 個小朋友站成一排。現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。
	 * 
	 *            每個小朋友都有一個不高興的程度。開始的時候,所有小朋友的不高興程度都是0。
	 * 
	 *            如果某個小朋友第一次被要求交換,則他的不高興程度增加1,如果第二次要求他交換,則他的不高興程度增加2(即不高興程度爲3),
	 *            依次類推。當要求某個小朋友第k次交換時,他的不高興程度增加k。
	 * 
	 *            請問,要讓所有小朋友按從低到高排隊,他們的不高興程度之和最小是多少。
	 * 
	 *            如果有兩個小朋友身高一樣,則他們誰站在誰前面是沒有關係的。 輸入格式 輸入的第一行包含一個整數n,表示小朋友的個數。
	 *            第二行包含 n 個整數 H1 H2 … Hn,分別表示每個小朋友的身高。 輸出格式
	 *            輸出一行,包含一個整數,表示小朋友的不高興程度和的最小值。 樣例輸入 3 3 2 1 樣例輸出 9 樣例說明
	 *            首先交換身高爲3和2的小朋友,再交換身高爲3和1的小朋友,再交換身高爲2和1的小朋友,每個小朋友的不高興程度都是3,
	 *            總和爲9。 數據規模和約定 對於10%的數據, 1<=n<=10; 對於30%的數據, 1<=n<=1000;
	 *            對於50%的數據, 1<=n<=10000; 對於100%的數據,1<=n<=100000,0<=Hi<=1000000。
	 */

	static int n;
	static int[] s;// 身高
	static int notHappy = 99999;

	/*
	 * 當前最高興的小朋友的值總的高興值
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		s = new int[n];
		int[][] count = new int[n][2];
		for (int i = 0; i < count.length; i++) {
			s[i] = sc.nextInt();
			count[i][0] = 0;
			count[i][1] = 0;
		}
		//long start = System.currentTimeMillis();
		int all = exchange(findMin(count), count);
		System.out.println(all);
	//	long end = System.currentTimeMillis();
		//print("此程序運行,花費的時間是" + ((end - start) / 1000.0) + "秒.");

	//	print("每個小朋友不高興的詳情信息");
	//	for (int i = 0; i < n; i++) {
	//		print(i + "不高興的值是=" + count[i][0]);
	//	}
	}

	/*
	 * 是否排好序
	 */
	public static boolean isOK() {
		for (int i = 0; i < s.length; i++) {
			for (int j = i + 1; j < s.length; j++) {
				if (s[j] < s[i]) {
					return false;
				}
			}

		}
		return true;
	}

	// 當前所有人不高興的值的總和
	public static int sum(int[][] count) {
		int temp = 0;
		for (int i = 0; i < count.length; i++) {
			temp += count[1][0];

		}
		return temp;
	}

	public static void print(Object o) {
		System.out.println(o.toString());
	}

	/**
	 * 
	 * @return 返回當前相對最高興的小朋友的下標值,(同事滿足身高左高右低的要求)
	 */
	public static int findMin(int[][] count) {
		int[] temp = new int[n];
		for (int i = 0; i < temp.length; i++) {
			temp[i] = count[i][1];
		}
		for (int i = 0; i < temp.length; i++) {
			int min = i;
			for (int j = i + 1; j < temp.length; j++) {
				if (temp[min] > temp[j]) {
					min = j;
				}
			}
			temp[min] = temp[i];
			if (min < n - 1 && s[min] > s[min + 1]) {
				return min;
			}
		}
		return n - 2;
	}

	/**
	 * 交換位置和不高興的值下標k和k+1的交換,同時不高興的值增加
	 * 
	 * @return
	 */
	public static void exchangeAll(int[][] count, int k) {
		int temp;
		temp = s[k + 1];
		s[k + 1] = s[k];
		s[k] = temp;

		count[k][1]++;
		count[k][0] = count[k][0] + count[k][1];

		count[k + 1][1]++;
		count[k + 1][0] = count[k + 1][0] + count[k + 1][1];

		int[] temp1 = count[k];
		count[k] = count[k + 1];
		count[k + 1] = temp1;
	}

	/*
	 * 交換位置,身高高的向右和身高低的可以換,否則下一個相鄰位置
	 * 
	 * @param k 前搜索的位置
	 * 
	 * @param count count[0][]當前每個交換的小朋友的不高興程度和count[1][]交換的次數
	 */

	public static int exchange(int k, int[][] count) {
		if (isOK()) {
			int temp = sum(count);
			if (temp < notHappy) {
				notHappy = temp;
			}
			return temp;
		} else {
			/** 交換身高,從最不高興的到最高興的開始搜索 */
			exchangeAll(count, k);
			//System.out.println("K&k+1=" + k + "   " + (k + 1));
			return exchange(findMin(count), count);
		}
	}

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章