• GHOJ 230 谁会赢?


    题目描述

            最近,在课余时间流行一种游戏,游戏的规则如下:游戏开始时,每个人都从规定范围内的数中选取一个数(保证所选取的数各不相同),写在纸上,握在手中(以防让别的同学看见),然后同时打开,如果其中一个同学手中的数是其他任意两位同学手中的数之和,那么他就赢,如果满足条件的有多个,手中的数最大的那位同学赢。这是心理和智力的双重考验,所以参加的学生越来越多。但是,由于参与人数众多,要判断谁赢就成了问题,请聪明的你设计一个程序来解决这个问题!

     

    输入输出格式

    输入格式

            第一行为一个整数N(3≤N≤50000),表示参加游戏的总人数,第二行为N个数(范围在0-231231之间),依次表示N个同学所选的数,第i个数表示第i位同学所选的数。

     

    输出格式

            一行,为一个整数,表示哪位同学赢。如果没有任何一位同学赢,则输出“0”。

    输入输出样例

    输入样例

    2 5 7 3 12

     

    输出样例

    5

    题解

            朴素做法是枚举两个数,然后判断第三个数是否存在,用map能做到O(n²logn)的时间复杂度,但显然这种做法不满足题目需求。

            我们可以换一个角度思考,枚举和,然后枚举加数,加数实际上可以用一重循环就枚举出来。我们可以排序之后不断缩小枚举区间来找加数,这样就能做到O(n²)的时间复杂度。具体看下面的代码。

    #include <iostream>
    #include <cstdio>
    
    #define MAX_N 50001
    
    using namespace std;
    
    int n;
    struct Node
    {
        int idx;
        int val;
    };
    Node a[MAX_N | 1], b[MAX_N | 1];
    
    void Sort(int lt, int rt)
    {
        if(lt == rt) return;
        int mid = lt + rt >> 1;
        Sort(lt, mid);
        Sort(mid + 1, rt);
        int i = lt, j = mid + 1;
        int cnt = 0;
        while(i <= mid && j <= rt)
        {
            if(a[i].val < a[j].val) b[++cnt] = a[i++];
            else b[++cnt] = a[j++];
        }
        while(i <= mid)
        {
            b[++cnt] = a[i++];
        }
        while(j <= rt)
        {
            b[++cnt] = a[j++];
        }
        for(i = 1; i <= cnt; ++i)
        {
            a[lt + i - 1] = b[i];
        }
        return;
    }
    
    int main()
    {
        scanf("%d", &n);
        for(register int i = 1; i <= n; ++i)    
        {
            scanf("%d", &a[i].val);
            a[i].idx = i;
        }
        Sort(1, n);
        for(register int k = n; k; --k)
        {
            for(register int i = 1, j = k - 1; i < j;)
            {
                while(i < j && a[i].val + a[j].val < a[k].val) ++i;
                while(i < j && a[i].val + a[j].val > a[k].val) --j;
                if(a[i].val + a[j].val == a[k].val) return printf("%d", a[k].idx), 0;
            } 
        }
        putchar('0');
        return 0;
    }
    参考程序O(n²)版

            我们可以发现,我们不断缩小的区间本身就是满足单调性的,那我们其实可以用二分查找来缩小区间,这样子在题库给的数据下更快,使时间复杂度降到O(OK)。

    #include <iostream>
    #include <cstdio>
    
    #define MAX_N 50001
    
    using namespace std;
    
    int n;
    struct Node
    {
        int idx;
        int val;
    };
    Node a[MAX_N | 1], b[MAX_N | 1];
    
    int Read()
    {
        int res = 0;
        char ch = getchar();
        while(ch < '0' || ch > '9') ch = getchar();
        while(ch >= '0' && ch <= '9') res = res * 10 + ch - '0', ch = getchar();
        return res;
    }
    
    void Sort(int lt, int rt)
    {
        if(lt == rt) return;
        int mid = lt + rt >> 1;
        Sort(lt, mid);
        Sort(mid + 1, rt);
        int i = lt, j = mid + 1;
        int cnt = 0;
        while(i <= mid && j <= rt)
        {
            if(a[i].val < a[j].val) b[++cnt] = a[i++];
            else b[++cnt] = a[j++];
        }
        while(i <= mid)
        {
            b[++cnt] = a[i++];
        }
        while(j <= rt)
        {
            b[++cnt] = a[j++];
        }
        for(i = 1; i <= cnt; ++i)
        {
            a[lt + i - 1] = b[i];
        }
        return;
    }
    
    int main()
    {
        n = Read();
        for(register int i = 1; i <= n; ++i)    
        {
            a[i].val = Read();
            a[i].idx = i;
        }
        Sort(1, n);
        int lt, rt, mid;
        int op;
        for(register int k = n; k; --k)
        {
            for(register int i = 1, j = k - 1; i < j;)
            {
                op = 0;
                if(a[i].val + a[j].val < a[k].val) 
                {
                    lt = i + 1;
                    rt = j - 1;
                    while(lt <= rt)
                    {
                        mid = lt + rt >> 1;
                        if(a[mid].val + a[j].val < a[k].val) lt = mid + 1;
                        else op = 1, i = mid, rt = mid - 1;
                    }
                }
                if(a[i].val + a[j].val > a[k].val) 
                {
                    lt = i + 1;
                    rt = j - 1;
                    while(lt <= rt)
                    {
                        mid = lt + rt >> 1;
                        if(a[i].val + a[mid].val > a[k].val) rt = mid - 1;
                        else op = 1, j = mid, lt = mid + 1;
                    }
                }
                if(a[i].val + a[j].val == a[k].val) 
                {
                    printf("%d", a[k].idx);
                    return 0;
                }
                if(!op) break;
            } 
        }
        putchar('0');
        return 0;
    }
    参考程序O(OK)版
  • 相关阅读:
    动软代码生成器 修改配置
    显示转换explicit和隐式转换implicit
    Memcache学习整理
    SQL2008-分页显示3种方法
    SQL2008-表对表直接复制数据
    SQL2008-删除时间字段重复的方法
    SQL2008-中不想插入从复记录
    SQL2008-c:PROGRA~1COMMON~1SystemOLEDB~1oledb32.dll出错找不到指定的模块
    ACCESS-如何多数据库查询(跨库查询)
    ACCESS-字符函数
  • 原文地址:https://www.cnblogs.com/kcn999/p/10353063.html
Copyright © 2020-2023  润新知