[NOIP模拟] Ball

题目描述:
题目背景: SOURCE:NOIP2015-SHY-9
Alice 与 Bob 在玩游戏。他们一共玩了 t 轮游戏。游戏中,他们分别获得了 n 个和 m 个小球。每个球上有一个分数。每个人的得分都为他所获得所有小球分数的乘积,分数小者获胜。问每轮游戏谁会获胜?请输出每轮游戏的胜者。数据保证不会出现平局,且两个人分数差异大于任意一个人分数的 1% 。
输入格式:
第一行为两人玩的轮数 t(1≤t≤10)。
每一轮游戏的输入中:
第一行一个整数 n,代表 Alice 获得球的个数。
第二行为 n 个整数 ai,代表 Alice 每个球的分数。
第三行一个整数 m,代表 Bob 获得球的个数。
第四行为 m 个整数 bi,代表 Bob 每个球的分数。
输出格式:
输出共 t 行,每行为该轮胜者的名字“Alice”或“Bob”。
样例输入:
1
3
2 3 4
4
1 3 4 5
样例输出:
Alice
样例说明:
Alice:2 * 3 * 4 = 24
Bob: 1 * 3 * 4 * 5 = 60
数据范围:
对于 40% 的数据:n,m,ai,bi≤10;
对于 100% 的数据:1≤n,m≤100000;-10000≤ai,bi≤10000。
题目分析:
首先明确:保证不相等且差异大于积的 1%,这样精度上就不用太担心。
此题有三种方法:
方法一:log法(标算的方法)首先我们知道 logaxy=logax+logay 。这样对于连乘就可以转化为分别取对数后相加,两者的大小关系是不变的。而对于正负性和0,单独判断。正负性根据负号个数的奇偶判断。而每次取对数都是对绝对值取对数,0直接忽略掉。
PS:c++数学库里自带log,头文件加入#include<cmath>;运用时log(x),底数是e。自带log还有log2(x)log10(x)(底数为2,精度相对最高),没有其它底数的log了。
方法二:相除法 你可以直接一个Alice一个Bob对应相除(短的就给它补1)得到商记做double型,把商乘起来比较与1的大小关系。正负性和0要记得判断。
方法三:极大值个数法你可以设一个极大值如 1016 ,直接连着乘,存入sum[tot],如果大于了 1016 ,就新开一个sum[++tot]存,以此类推,最后比较一下个数,显然,个数多的大,对于个数相同的,可以利用方法二把sum对应相除比较大小。同样记得判正负性和0。
附代码:
方法一:log法
此代码中用到了pair(更简洁),不知道的请百度一下。实际上pair就是一个包含两个数据值的结构体,第一个的名字是first,第二个是second。

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std;

int n,t,x;

int readint()
{
    char ch;int i=0,f=1;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') {ch=getchar();f=-1;}
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
    return i*f;
}

pair<int,double> find()//pair型的第一维处理正负号和0,第二维处理绝对值取对数的和
{
    pair<int,double> result=make_pair(1,0);//两维分别赋初值1,0
    n=readint();
    for(int i=1;i<=n;i++)
    {
        x=readint();
        if(x<0)
        {
            result.first=-result.first;
            result.second+=log(-x);
        }
        else
        {
            if(x==0) result.first=0;
            else result.second+=log(x);
        }
    }
    return result;
}

int main()
{
    //freopen("lx.in","r",stdin);

    t=readint();
    while(t--)
    {
        pair<int,double> x1=find();
        pair<int,double> x2=find();
        double ans1=x1.first*x1.second;//这样相乘就可以方便地直接比较大小了
        double ans2=x2.first*x2.second;
        if(ans1<ans2) printf("Alice\n");
        else printf("Bob\n");
    }

    return 0;
}

方法二:相除法
直接copy的一位同学的

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
using namespace std;
inline int read()
{
    int i=0,f=1;
    char ch;
    ch=getchar();
    while(ch>'9'||ch<'0')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        i=(i<<1)+(i<<3)+ch-'0';
        ch=getchar();
    }
    return i*f;
}
int t,n,m,a[100005],b[100005];
double c[100005];
int main()
{
    t=read();
    while(t--)
    {
        n=read();
        int f1=0,f2=0;
        for(int i=1;i<=n;i++)
        {
            a[i]=read();
            if(a[i]<0)
            {
                f1++;
                a[i]=-a[i];
            }
        }
        m=read();
        for(int i=1;i<=m;i++)
        {
            b[i]=read();
            if(b[i]<0)
            {
                f2++;
                b[i]=-b[i];
            }
        }   
        if(f1%2==1&&f2%2==0)
        {
            cout<<"Alice"<<endl;
            continue;
        }
        if(f1%2==0&&f2%2==1)
        {
            cout<<"Bob"<<endl;
            continue;
        }
        bool bz;
        if(f1%2==0&&f2%2==0)bz=1;
        if(f1%2==1&&f2%2==1)bz=0;
        sort(a+1,a+n+1);
        sort(b+1,b+n+1);
        int p=0;
        for(int i=1;i<=max(n,m);i++)
        {
            if(i>m)b[i]=1;
            if(i>n)a[i]=1;
            c[i]=double(a[i])/double(b[i]);
            if(a[i]==0)
            {
                p=1;
                break;
            }
            if(b[i]==0)
            {
                p=2;
                break;              
            }
        }
        if(p==1)
            if(f2%2==0)
            {
                cout<<"Alice"<<endl;
                continue;
            }
        if(p==2)
        {
            if(f1%2==0)
            {
                cout<<"Bob"<<endl;
                continue;               
            }
            else
            {
                cout<<"Alice"<<endl;
                continue;               
            }           
        }
        sort(c+1,c+max(n,m)+1);
        double ans=1;
        bool bj=0;
        for(int i=1;i<=max(n,m);i++)
        {
            ans*=c[i];
            if(ans>1)
            {
                bj=1;
                break;
            }
        }
        if(bj)
            if(!bz)cout<<"Alice"<<endl;
            else cout<<"Bob"<<endl;
        else
            if(bz)cout<<"Alice"<<endl;
            else cout<<"Bob"<<endl;     
    }
    return 0;
}

方法三:极大值个数法
我考试时自己写的

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std;

const int N=1e5+10;
const long long INF=1e16;
int check1,check2,t,n,m,check,tot1,tot2;
double sh[N],ans;
long long sum1[N],sum2[N],a[N],b[N];
bool flag1,flag2;

int readint()
{
    char ch;int i=0,f=1;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-') {ch=getchar();f=-1;}
    for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0';
    return i*f;
}

int main()
{
    //freopen("ball.in","r",stdin);
    //freopen("ball.out","w",stdout);

    t=readint();
    while(t--)
    {
        check1=0;check2=0;check=0;tot1=1;tot2=1;flag1=false;flag2=false;
        ans=1;
        n=readint();
        for(int i=1;i<=n;i++)
        {
            a[i]=readint();
            if(a[i]<0)
            {
                check1=check1^1;//用异或判断符号个数的奇偶
                a[i]=abs(a[i]);
            }
            if(a[i]==0) flag1=true;//flag判断是否有0
        }
        m=readint();
        for(int i=1;i<=m;i++)
        {
            b[i]=readint();
            if(b[i]<0)
            {
                check2=check2^1;
                b[i]=abs(b[i]);
            }
            if(b[i]==0) flag2=true;
        }
        if(flag1==false&&flag2==false)//均不含0
        {
            if(check1==1&&check2==0) {printf("Alice\n");continue;}
            if(check1==0&&check2==1) {printf("Bob\n");continue;}
        }
        if(flag1==true&&flag2==false)//Alice为0 ,Bob不为0
        {
            if(check2==1) {printf("Bob\n");continue;}
            else {printf("Alice\n");continue;}
        }
        if(flag1==false&&flag2==true)//Alice不为0 ,Bob为0
        {
            if(check1==1) {printf("Alice\n");continue;}
            else {printf("Bob\n");continue;}
        }
        sum1[1]=1;sum2[1]=1;
        for(int i=1;i<=n;i++)
        {
            sum1[tot1]*=a[i];
            if(sum1[tot1]>INF) {tot1++;sum1[tot1]=1;}
        }
        for(int i=1;i<=m;i++)
        {
            sum2[tot2]*=b[i];
            if(sum2[tot2]>INF) {tot2++;sum2[tot2]=1;}
        }
        if(tot1>tot2) check=1;//比较个数
        if(tot1<tot2) check=2;
        if(tot1==tot2)
        {
            if(tot1==1)
            {
                if(sum1[1]<sum2[1]) check=2;
                else check=1;
            }
            else
            {
                for(int i=1;i<=tot1;i++)
                    sh[i]=(sum1[i]*1.0)/sum2[i];//相除
                for(int i=1;i<=tot1;i++)
                    ans*=sh[i];
                if(ans<1) check=2;
                else check=1;       
            }
        }
        if(check==2)
        {
            if(check1==0&&check2==0) {printf("Alice\n");continue;}
            if(check1==1&&check2==1) {printf("Bob\n");continue;}
        }
        else
        {
            if(check1==0&&check2==0) {printf("Bob\n");continue;}
            if(check1==1&&check2==1) {printf("Alice\n");continue;}
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值