第七届蓝桥杯省赛C++A/B组 最大比例
X星球的某个大奖赛设了 M 级奖励。
每个级别的奖金是一个正整数。
并且,相邻的两个级别间的比例是个固定值。
也就是说:所有级别的奖金数构成了一个等比数列。
比如:16,24,36,54,其等比值为:3/2。
现在,我们随机调查了一些获奖者的奖金数。
请你据此推算可能的最大的等比值。
输入格式
第一行为数字 N,表示接下的一行包含 NN 个正整数。
第二行 N 个正整数 Xi,用空格分开,每个整数表示调查到的某人的奖金数额。
输出格式
一个形如 A/B的分数,要求 A、B 互质,表示可能的最大比例系数。
数据范围
0<N<100
0<Xi<1012
数据保证一定有解。
输入样例1:
3
1250 200 32
输出样例1:
25/4
输入样例2:
4
3125 32 32 200
输出样例2:
5/2
输入样例3:z
3y
549755813888 524288 2
输出样例3:
4/1
我们假定首相为a,公比为q,那么对于S的每一项都是a*qx,对于S去重后从小到大排列的情况,后一项除以前一项就能消掉了a,我们得到了n-1个qn的新数列(我们即为S1),我们可以重复上述操作(去重,排序),直到只剩下一个qn的值,我们记为p,那我们如何确定q的值呢?
当我们确定了p的值后,对于我们将原数列从第二项开始每项除以第一项,这样能得到的数列origin每一个也是qx,(如果第一项是a*qx',确实后面的每项实际上是多除以了qx'的,但是这样可能造成q求得大了,例如a=3,q=2,第一项是6,接下来几项是24,96,384,这样q求出来是4,but,我们可以把a当成6,那么这样公比就为4了,这样4大于2而且也符合题意)那么我们通过S1的每个qn去不断除以p(因为p是S1经过多次后一项除以前一项得来的,所以p会小于S1的所有),直到出现S1的某项多次除以p后结果不为1且小于p,再次强调,p是qx,S1的每一项是qy,如果S1的某项多次除以p后不能整除且剩余的小于p,则说明剩余的项qz(z = y % x,因为每次除以qx,而qy/qx=qy-x),则此时的z是小于x的,通过S1数列的所有项都如此操作,就得到了q。
提问:最后的z会不会不为1,导致算出来的不是q?
答:如果不为1,那么说明所有的项均以qz为最大公比,那不就相当于我们一开始假设q为qz是一样的情况了。
提问:如果求得最后的z不是p的次方,那之前每次除以p不是错误的操作了嘛,不得从头除以现在算出来的qz嘛?
答:既然p是通过操作求解得到的,那么p一定是最大公比q的n次幂,我们求得了最新的qz一定是q的幂,所以最后求出最小的一定是q,那么之前是除以q还是p(p也是q的n次幂)都没有关系了。
AC代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll INF = 0x3f3f3f3f3f3f3f3f; struct Node{//构造分数结构体 ll son, fa; Node(ll temp){ fa = 1; son = temp; } Node(ll x, ll y){//x是分子,y是分母 ll gcd = __gcd(x, y); son = x / gcd; fa = y / gcd; } Node(){} friend Node operator / (Node x, Node y){//分数除法 ll gcd1 = __gcd(x.son, y.son); x.son /= gcd1; y.son /= gcd1; ll gcd2 = __gcd(x.fa, y.fa); x.fa /= gcd2; y.fa /= gcd2; return Node(x.son * y.fa, y.son * x.fa); } friend bool operator == (Node x, Node y){//分数判断相等 return x.son == y.son && x.fa == y.fa; } friend bool operator < (Node x, Node y){//分数比较 ll lcd = x.fa / __gcd(x.fa, y.fa) * y.fa; x.son *= lcd / x.fa; y.son *= lcd / y.fa; return x.son < y.son; } }; vector<Node> v, origin;//v是原数列,origin是保留最后的S1数列 vector<Node> t;//t是在原数列上得到的新数列 Node ans; int main() { int n; ll temp; cin >> n; for(int i = 0; i < n; i++) { cin >> temp; v.push_back(Node(temp)); } v.erase(unique(v.begin(), v.end()), v.end());//删除重复 sort(v.begin(), v.end());//排序 //求解origin数列 origin = v; for(int i = 1; i < origin.size(); i++) { origin[i] = origin[i] / origin[0]; } while(v.size() != 1)//如果只剩一个了,跳出 { for(int i = 1; i < v.size(); i++) { t.push_back(v[i] / v[i - 1]);//后一个除以前一个分数 } v = t; t.clear(); v.erase(unique(v.begin(), v.end()), v.end()); sort(v.begin(), v.end()); } ans = v[0]; for(int i = 1; i < origin.size(); i++)//开始 { while(!(origin[i] == Node(1))) { if(ans < origin[i] || origin[i] == ans) origin[i] = origin[i] / ans; else//如果剩下的小于ans,就说明ans可以更小,我们更新ans ans = origin[i]; } } printf("%lld/%lld\n", ans.son, ans.fa); return 0; }