蓝桥杯2016c++A组真题&代码第十题最大比例
/*
最大比例
X星球的某个大奖赛设了M级奖励。每个级别的奖金是一个正整数。
并且,相邻的两个级别间的比例是个固定值。
也就是说:所有级别的奖金数构成了一个等比数列。比如:
16,24,36,54
其等比值为:3/2
现在,我们随机调查了一些获奖者的奖金数。
请你据此推算可能的最大的等比值。
输入格式:
第一行为数字 N (0<N<100),表示接下的一行包含N个正整数
第二行N个正整数Xi(Xi<1 000 000 000 000),用空格分开。每个整数表示调查到的某人的奖金数额
要求输出:
一个形如A/B的分数,要求A、B互质。表示可能的最大比例系数
测试数据保证了输入格式正确,并且最大比例是存在的。
例如,输入:
3
1250 200 32
程序应该输出:
25/4
再例如,输入:
4
3125 32 32 200
程序应该输出:
5/2
再例如,输入:
3
549755813888 524288 2
程序应该输出:
4/1
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 3000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。
*/
思路:
看代码注释
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
typedef long long ll;
using namespace std;
int N;
const int MAXN = 110;
ll arr[MAXN];
map<ll,map<ll,ll> > all_ex; // all_ex[x][y] = i , 对 x开方 y 次 的值
map<ll,map<ll,ll> > all_log;// all_log[x][y] = j y 的 j 次 为 x
//对于 开方 和 取对数,可以通过生成再进行查找,从而加快速度
void init(){
for(int i=2;i<1e6;i++){
ll cur = (ll)i*i;
ll pow = 2;
while(cur<1e12){
all_ex[cur][pow] = i; // 对 cur 开 pow 次方 的答案 为 i
all_log[cur][i] = pow;// 对 i 的 pow 次方是 cur log i cur = pow
pow++;
cur*=i;
}
}
}
struct ratio{
ll x,y;
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
//两者除以最小公倍数,则获得对应的互质的比值
ratio(ll x,ll y):x(x),y(y){
ll max_s = gcd(x,y);
this->x/=max_s;
this->y/=max_s;
}
};
vector<ratio> ra_arr;
//思路:
//1.从小到大排序
//2.对第一对比值和后面的 比值进行比较,如果能够保证前面的比值是后面所有比值的基数
//那么就可以得到答案
// 对 x 进行 pow 次开方
ll extract(ll x, ll pow){
// 开 1次方 就是本身
//对 1 进行任何开方 都是1
if(x ==1 ||pow == 1 ) return x;
//对map 进行查找,如果能够找到 对 x 进行pow 次开方 ,则返回结果
if(all_ex[x].find(pow)!= all_ex[x].end()){
return all_ex[x][pow];
}
//表示开方失败
return -1;
}
// 对 temp_x 取 以 base_x 为 底 的对数
ll log(ll temp_x,ll base_x){
if(base_x == temp_x) return 1;
if(all_log[temp_x].find(base_x) != all_log[temp_x].end()){
return all_log[temp_x][base_x];
}
return -1;
}
int main(){
init();
scanf("%d",&N);
for(int i =0;i<N;i++){
scanf("%lld",&arr[i]);
}
sort(arr,arr+N);
for(int i =0;i<N-1;i++){
//去重
if(arr[i]==arr[i+1]) continue;
ra_arr.push_back(ratio(arr[i+1],arr[i]));
}
//特殊情况
if(N==2){
ratio temp = ratio(arr[1],arr[0]);
printf("%lld/%lld\n",temp.x,temp.y);
return 0;
}
//最小的情况 为 2为基数, 最多开 40次 因为 2^40 超过 1e12
for(int i=1;i<=40;i++){
// 取第一个基数对
ll x = ra_arr[0].x;
ll y = ra_arr[0].y;
//对 x 开 i 次方 得到基数
ll base_x = extract(x,i);
ll base_y = extract(y,i);
//无法开方
if(base_x == -1 || base_y == -1 ) continue;
bool all_match = true;
//用第一对基数对所有的比值对进行比较
for(int j=0;j<ra_arr.size();j++){
ll temp_x = ra_arr[j].x;
ll temp_y = ra_arr[j].y;
//取对数,这样才能保证是基数关系
ll log_x = log(temp_x,base_x);
ll log_y = log(temp_y,base_y);
// temp_y 和 base_y 可能有出现1 的情况,对此开方没有意义
if(temp_y ==1 && base_y == 1) log_y = log_x;
if(log_y == -1||log_x == -1||log_x != log_y ){
all_match = false;
break;
}
}
if(all_match){
printf("%lld/%lld\n",base_x,base_y);
return 0;
}
}
return 0;
}