算法系列之小朋友排队问题

小朋友排队

问题描述

  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);
}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪潮之巅的小萝卜头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值