算法系列之小朋友排隊問題

小朋友排隊

問題描述

  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。

就是這道題,我用了整整一下午的時間,大量的時間浪費在眼瞎(審題不準),小夥伴要引以爲戒~

先說說錯誤的做法(不能說錯誤的做法吧,只是題目的另一種模式的答案):

 

沒看到這關鍵幾個字

思路:創建三個數組,一個存放原始數據,一個用於存放小朋友的不高興指數,另一個數組用於克隆原始數據改變之前的數據,進行比較,其實是按照位置來的,只要是位置上的數據發生改變,就累加這個位置小朋友的不高興指數。(代碼有註釋,可以參考)。

認爲只要交換位置就可以了,於是就有了洋洋灑灑的寫了下面的代碼:

小朋友排隊不相鄰(錯誤做法)

package 歷屆題目;

import java.util.Scanner;

public class 小朋友排隊不相鄰 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n=sc.nextInt();//表示小朋友的數量
//用於儲存小朋友的身高
int a[]=new int[n];
for(int i=0;i<n;i++){
a[i]=sc.nextInt();
}
sc.close();
//用於存放每個小朋友的不高興度
int b[]=new int[n];
//用於存放爲交換之前的數據
int c[]=a.clone();
long count=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(a[i]>a[j]){
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
for(int k=0;k<=j;k++){
if(c[k]==a[k]){
continue;
}else{
b[k]+=1;
count+=b[k];
}
}
c=a.clone();
}
}
System.out.println(count);
}
}
結果只有初始數據正確,其他的實驗數據不正確~

思路:之後又讀了一遍題,纔看到相鄰兩個字,在原來代碼基礎上進行了略微的修改,使得之前的不相鄰的交換,換成了相鄰的交換,又開始奮筆疾書寫了下面的代碼,下面的代碼會死循環的情況(儘量避免了,有跳出語句),題目所給的實驗數據是正確的,由於考慮的按照位置的不高興程度來進行累加,而不是按照題目中說的按照小朋友的不高興指數,進而導致最後的結果錯誤,想了很久怎麼解決這個問題,折磨了我很久,最後終於想到了以前經常用的逆序法,心中豁然開朗~

小朋友排隊相鄰(錯誤做法)

package 歷屆題目;

import java.util.Arrays;
import java.util.Scanner;

public class 小朋友排隊相鄰 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n=sc.nextInt();//表示小朋友的數量
//用於儲存小朋友的身高
int a[]=new int[n];
for(int i=0;i<n;i++){
a[i]=sc.nextInt();
}
sc.close();
//用於存放每個小朋友的不高興度
int b[]=new int[n];
//用於存放爲交換之前的數據
int c[]=a.clone();
int d[]=a.clone();
long count=0;
Arrays.sort(d);
while(true){
int num=0;
for(int i=0;i<n;i++){
if(a[i]==d[i]){
num++;
}
}
if(num==n){
break;
}
for(int i=0;i<n-1;i++){
if(a[i]>a[i+1]){
int temp=a[i];
a[i]=a[i+1];
a[i+1]=temp;
for(int k=i;k<=i+1;k++){
if(c[k]==a[k]){
continue;
}else{
b[k]+=1;
count+=b[k];
}
}
c=a.clone();
}
System.out.println(Arrays.toString(a));//測試代碼
}
}
System.out.println(Arrays.toString(a));//測試代碼
System.out.println(count);
}
}

 

小朋友排隊相鄰逆序法(正解但超時超過了1s)

思路:只要循環檢查每個小朋友前面有幾個比他高的,後面有幾個比他矮的,將兩者存放在不同的數組中,兩者的和就是這個小朋友需要交換的次數,然後用一個for循環就可以求出這個小朋友的不高興指數,進而將所有小朋友的不高興指數進行累加,就可以得到正解,好不容易有了思路,由於思路清晰,很快的將代碼完成了,興致勃勃的提交了代碼~一個運行時間超時,調到界面上,生無可戀啊,看來不是一個最優解,只能再努力吧。

package 歷屆題目;

import java.util.Scanner;

public class小朋友排隊相鄰逆法{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n=sc.nextInt();//表示小朋友的數量
//用於儲存小朋友的身高
int a[]=new int[n];
for(int i=0;i<n;i++){
a[i]=sc.nextInt();
}
sc.close();
f(a,n);

}
public static void f(int a[],int n){
//存放前者比小朋友高的人數數據
int before[]=new int[n];
//存放後者比小朋友低的人的數據
int after[]=new int[n];
for(int i=0;i<n;i++){
//存放前者比小朋友高的人數數據
for(int j=0;j<i;j++){
if(a[i]<a[j]){
before[i]+=1;
}
}
//存放後者比小朋友低的人的數據
for(int j=i+1;j<n;j++){
if(a[i]>a[j]){
after[i]+=1;
}
}
}
sum(before,after,n);
}
public static void sum(int before[],int after[],int n){
int sum=0;
for(int i=0;i<n;i++){
for(int j=1;j<=before[i]+after[i];j++){
sum+=j;
}
}
System.out.println(sum);
}
}

小朋友排隊不超時(正解)

已經無力吐槽了,這一天過得,程序員真心累啊~還得用到類和對象,以及內部類~

package 歷屆題目;
import java.util.Scanner;
public class 小朋友排隊不超時正解 {

public static int n;
public static person[] Child;
public static long result = 0;

static class person {
public int high; //身高
public long count; //調換次數

public person(int high) {
this.high = high;
this.count = 0;
}
}

public void mergeSort(person[] A) {
if(A.length > 1) {
person[] leftA = getHalf(A, 0);
person[] rightA = getHalf(A, 1);
mergeSort(leftA);
mergeSort(rightA);
Merge(A, leftA, rightA);
}
}

public person[] getHalf(person[] A, int judge) {
int len = A.length;
person[] half;
if(judge == 0) {
half = new person[len / 2];
for(int i = 0;i < half.length;i++)
half[i] = A[i];
} else {
half = new person[len - len / 2];
for(int i = 0;i < half.length;i++)
half[i] = A[len / 2 + i];
}
return half;
}

public void Merge(person[] A, person[] leftA, person[] rightA) {
int i = 0, j = 0;
int lenL = leftA.length, lenR = rightA.length;
while(i < lenL && j < lenR) { //計算leftA中大於rightA[j]的元素個數
if(leftA[i].high > rightA[j].high) {
rightA[j].count += (lenL - i);
j++;
} else {
i++;
}
}
i = lenL - 1;
j = lenR - 1;
while(i >= 0 && j >= 0) { //計算rightA中小於leftA[i]的元素個數
if(leftA[i].high > rightA[j].high) {
leftA[i].count += (j + 1);
i--;
} else {
j--;
}
}
//進行歸併排序,從小到大排序
i = 0;
j = 0;
int t = 0;
while(i < lenL && j < lenR) {
if(leftA[i].high < rightA[j].high)
A[t++] = leftA[i++];
else
A[t++] = rightA[j++];
}
while(i < lenL)
A[t++] = leftA[i++];
while(j < lenR)
A[t++] = rightA[j++];
}


public static void main(String[] args) {
小朋友排隊不超時正解 test = new 小朋友排隊不超時正解();
Scanner in = new Scanner(System.in);
n = in.nextInt();
Child = new person[n];
for(int i = 0;i < n;i++) {
int high = in.nextInt();
Child[i] = new person(high);
}
test.mergeSort(Child);
for(int i = 0;i < n;i++) {
long count = Child[i].count;
result += count * (count + 1) / 2;
}
System.out.println(result);
}
}

 

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