目录
题目描述
恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。
输入描述:
第一行包含一个整数 n ,表示大臣的人数。 第二行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示国王左手和右手上的整数。 接下来 n 行,每行包含两个整数 a 和 b ,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。
输出描述:
一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。
这道题,注意事项:
1.我们需要返回这个排列中获奖赏最多的大臣所获得的金币数。但要使获奖赏最多的大臣的金币数尽可能的少,所以我们需要改变排列顺序。
2.因为是不断的乘积,然后做一次除法,结果可能会变得非常大,所以需要用BigInteger类来完成,这个类使用时,本身就会申请一个栈空间,所以如果用快排等递归排序算法,会造成栈溢出。
如何排列最优?
第一种方法 时间复杂度较复杂(跑的时候会出现运行超时,可以拿部分分,但最容易想到)
思路:使用冒泡排序,如果能使我的最大结果变小就交换,(为什么能这样交换,因为交换相邻两个大臣之后只影响两个大臣的结果,不会影响其他大臣的结果,所以可以使用冒泡排序一直找到能交换的情况)最后就能得到最优解,但时间复杂度过大,这里不给出代码。
第二种方法:更新交换策略,还是使用冒泡,因为上一个方法在循环时进行了复杂的比较,转换操作,导致时间复杂度变大。
思路:
通过数学可证明大臣左右手乘积小的排在前面,这样就是最优排序策略。
代码如下:
import java.util.*;
import java.math.*;
public class Main{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
int n = input.nextInt();
BigInteger[][] num = new BigInteger[n+1][2];
for(int i = 0; i <= n; i++){
num[i][0] = input.nextBigInteger();
num[i][1] = input.nextBigInteger();
}
System.out.println(searchMin(num));
}
public static BigInteger searchMin(BigInteger[][] num){
BigInteger min = BigInteger.valueOf(-1);
BigInteger total = BigInteger.valueOf(1);
for(int i = 1; i < num.length - 1; i++){
for(int j = 1; j < num.length-i; j++){
BigInteger temp1 = num[j][1].multiply(num[j][0]);
BigInteger temp2 = num[j+1][1].multiply(num[j+1][0]);
if (temp1.compareTo(temp2) == 1){
swap(num, j, j+1);
}
}
}
total = num[0][0];
for (int i = 1; i<num.length;i++){
BigInteger res = total.divide(num[i][1]);
min = min.max(res);
total = total.multiply(num[i][0]);
}
return min;
}
public static void swap(BigInteger[][] num, int i, int j){
BigInteger temp1 = num[i][1];
BigInteger temp = num[i][0];
num[i][1] = num[j][1];
num[i][0] = num[j][0];
num[j][1] = temp1;
num[j][0] = temp;
}
}