前言
期末之前做的题目,考完后又在赶大作业,又玩了几天,今天终于腾出时间来仔细回顾一下这道题了。
题目
题目描述
恰逢 H 国国庆,国王邀请 n n n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n n n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。
输入格式
第一行包含一个整数 n n n,表示大臣的人数。
第二行包含两个整数 a a a 和 b b b,之间用一个空格隔开,分别表示国王左手和右手上的整数。
接下来 n n n 行,每行包含两个整数 a a a 和 b b b,之间用一个空格隔开,分别表示每个大臣左手和右手上的整数。
输出格式
一个整数,表示重新排列后的队伍中获奖赏最多的大臣所获得的金币数。
样例 #1
样例输入 #1
3
1 1
2 3
7 4
4 6
样例输出 #1
2
提示
【输入输出样例说明】
按 1 1 1、 2 2 2、 3 3 3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 2 2;
按 1 1 1、 3 3 3、 2 2 2 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 2 2;
按 2 2 2、 1 1 1、 3 3 3 这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 2 2;
按$ 2$、 3 3 3、$1 $这样排列队伍,获得奖赏最多的大臣所获得金币数为 9 9 9;
按 3 3 3、 1 1 1、$2 $这样排列队伍,获得奖赏最多的大臣所获得金币数为 2 2 2;
按$ 3$、 2 2 2、 1 1 1 这样排列队伍,获得奖赏最多的大臣所获得金币数为 9 9 9。
因此,奖赏最多的大臣最少获得 2 2 2 个金币,答案输出 2 2 2。
【数据范围】
对于 20 % 20\% 20% 的数据,有 1 ≤ n ≤ 10 , 0 < a , b < 8 1≤ n≤ 10,0 < a,b < 8 1≤n≤10,0<a,b<8;
对于 40 % 40\% 40% 的数据,有$ 1≤ n≤20,0 < a,b < 8$;
对于 60 % 60\% 60% 的数据,有 1 ≤ n ≤ 100 1≤ n≤100 1≤n≤100;
对于 60 % 60\% 60% 的数据,保证答案不超过 1 0 9 10^9 109;
对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 1 , 000 , 0 < a , b < 10000 1 ≤ n ≤1,000,0 < a,b < 10000 1≤n≤1,000,0<a,b<10000。
题目分析
明显就是贪心然后涉及到一个排序。贪心算法不难猜出,肯定没有直接左手排序或者右手排序这么简单,结合题意不难猜到是两手之积的大小排序。
但其实难点在于高精度的运算,整体结构是采用结构体,分别存储了left左手;right右手;money就是这个大臣他获得的奖赏,因为是高精度所以是一个数组;total就是存储前面的累计乘积,也是一个高精度数组;还有一个lens是money的长度,因为数组并不能很容易反应长度,所以增加一个这个方便比较money的大小。
整体的思路是写入后,先按照左右收乘积和排序,这个就是能够使得最大的奖赏最小的排序了,接着就是要计算到底哪个是最大的。所以要把所有的人的奖赏都算出来,要算任意一个人的奖赏需要先算出之前大臣奖赏的累计乘积,再除以该大臣的右手数值。因为在数值大的时候,累计乘积将超过long long的限制,所以乘法、除法和比较都需要采用高精度。
所以设计高精度的主要是三个:1.高精度比较–>最后需要看谁最大(不一定是最后一个最大,自己假设一种最后一个右手数值很大的情况就知道)。2.高精度乘法–>计算累计乘积(但是这里有个可以节省的地方是,因为我们已经知道上一个的累计乘积,所以我们计算仅需要再乘以一个左手值即可,有点像动态规划的存储了)。3.高精度除法–>也是大数除以小数。
理好思路,然后就可以逐个击破了,分别写出实现三个功能的函数,这里直接使用结构体作为形式参数,方便赋值等事项。当然也需要分清a和b接着就没啥问题了。
思路来源参考
注意事项
- 第一个是国王不算大臣。
- 容易混淆a和b,以及啊。total和money。
- total不包括当前大臣的左手,就是
minister[n].total=minister[0].left*minister[1].left*...*minister[n-1].left
代码
AC图镇帖。
#include<iostream>
#include<algorithm>
using namespace std;
struct ministers {
int left,right,lens;
int money[5007]= {0};
int total[5007]= {0};
} minister[1007],res;
int point;
bool cmp(ministers a, ministers b) //cmp函数
{
return a.left*a.right < b.left*b.right;
}
bool cmp2(ministers a,ministers b) //高精度比较
{
if(a.lens!=b.lens)
return a.lens>b.lens;
else {
int i;
for(i=a.lens-1; i>=0; i--)
if(a.money[i]==b.money[i]) {
continue;
} else return a.money[i]>b.money[i];
}
return true;
}
int len(int a[])
{
int l=5006;
while(l) {
if(a[l])
break;
l--;
}
return l+1;
}
struct ministers gjcheng(ministers a,ministers b)
{
// 需要实现这样的功能b.total=a.total*b.left;
int k=len(a.total);
for(int i=0; i<k; i++) {
b.total[i]=a.total[i]*b.left;
}
int i=0;
do {
b.total[i+1]+=b.total[i]/10;
b.total[i]=b.total[i]%10;
i++;
} while(b.total[i]>=10||i<k);
return b;
}
struct ministers gjchu(ministers a,ministers b)
{
// 需要实现这样的功能b.money=a.total/b.right;
int r=0;
int k=len(a.total)-1;
for (int i = k; i >= 0; i -- ) {
r = r * 10 + a.total[i];
b.money[i]=r / b.right;
r %= b.right;
}
b.lens=len(b.money);
return b;
}
int main()
{
int n,temp;
cin>>n;
cin>>minister[0].left>>minister[0].right;//国王的
temp=minister[0].left;
point=0;
while(temp!=0) {
minister[0].total[point++]=temp%10;
temp/=10;
}
for(int i = 1 ; i <= n; i++) {
cin>>minister[i].left>>minister[i].right;//大臣的
}
sort(minister+1,minister+n+1,cmp);
for(int i = 1 ; i <= n; i++) {
minister[i]=gjchu(minister[i-1],minister[i]);
minister[i]=gjcheng(minister[i-1],minister[i]);;
}
res.lens=-1;
for(int i = 1 ; i <= n; i++) {
if(cmp2(minister[i],res))
res=minister[i];
}
for(int i = res.lens-1 ; i >=0 ; i--)
cout<<res.money[i];
return 0;
}
后话
太久没有写题解而忘了大半当时的思路了。但是这道题确实花费我不少时间。高精度的东西原理容易懂但是实现起来不是那么容易。
测试用例
给大家一个原题的测试用例,方便大家进行测试
输入
15
7 2
1 6
4 5
5 1
1 6
4 1
5 3
5 2
2 4
4 3
5 5
7 4
4 5
1 3
5 4
6 6
输出
13066666
王婆卖瓜
感觉有收获或者想跟上我的进度刷题的,可以点个关注,或者点赞收藏评论都可以!
题目来源
NOIP 2012 提高组 第一天 第二题
洛谷链接
(本文简介由GPT生成)
封面取自游戏《刺杀国王》