zoukankan      html  css  js  c++  java
  • 电信学院第一届新生程序设计竞赛题解及std

    首先非常感谢各位同学的参加,还有出题验题同学的辛勤付出

    昨天想偷懒就是不想再把我C++11的style改没了,大家看不懂的可以百度一下哦,懒得再写gcc了,毕竟代码是通的

    //代表的是行注释,所以那个读入文件和输出到文件就不用问我啦

    题目类型一览

    A Kannyi的数字密码 (模拟&&复杂的循环||手算)
    B Kannyi爱干净(注意变量初始化||set)
    C Kannyi的正方体和圆柱体(输入输出签到,PI已提示)
    D kannyi的独木桥(max和min)
    E Kannyi的简单检查 (循环签到 注意'-')
    F Kannyi的倒计时(a+b签到)
    G kannyi的开矿规划(大力模拟)
    H Kannyi的闯关游戏(BFS 两次最短路)
    I Kannyi爱种树(线段相交)
    J Kannyi 的easy problem((数学||瞎猜)&&长整型)
    K Kannyi的复旦(二分||stl)

    5619: Kannyi的数字密码 分享至QQ空间

    时间限制(普通/Java):1000MS/3000MS     内存限制:65536KByte
    总提交: 80            测试通过:30

    描述

     

    今天Kannyi说他要生成一些他的密码,他会选择Smith数作为他不同账号的密码。

    Smith数是这样定义的:一个数的各位之和等于其所分解的素因子各位数字之和。例如378就是Smith数,因为378=2×3×3×3×7,而且3+7+8=2+3+3+3+7。在这个定义中,这些素因子也要拆成各位数字再求和,例如22=2*11,且2+2=2+1+1,所以22也是Smith数。现在要去掉质数,并将剩下的数重新命名为kannyi数。

    现在给你一个a,请输出第a个Kannyi数。

    输入

     

    输入数据包含多组测试实例,每个测试实例占一行,为一个正整数a (1 ≤ a ≤ 30)。

    输出

     

    每个测试样例占一行,为第a个Kannyi数。

    样例输入

     3

    样例输出

     27

    提示

    OJ的评测机为windows,如需使用长整数(long long或__int64),输入输出请使用%I64d,本次比赛不再提示。

    这个题目应该还好吧,满足要求的数非常之少,甚至可以手算出来

    当然你也可以去写这个循环,数位之和就是个进制转换

    #include<bits/stdc++.h>
    using namespace std;
    //以上为c++11的万能头文件,十分方便好用
    int sum_of_digit(int n)//把代码封装成函数,好用,好理解,减少代码冗余
    {
        int sum=0;
        while(n)sum+=n%10,n/=10;
        return sum;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        int n;
        while(cin>>n)//C++的输入,这样可以自己判断EOF,因为读到EOF的返回值为0
        {
            int num=0;
            for(int i=0;;i++)
            {
                int sum=sum_of_digit(i),t=i;
                for(int j=2;j<i;j++)
                {
                    if(t%j==0)
                    {
                        while(t%j==0)
                        {
                            sum-=sum_of_digit(j);
                            t/=j;
                        }
                    }
                }
                if(t==i||i==2)continue;
                if(sum==0)num++;
                if(num==n)
                {
                    cout<<i<<"
    ";//C++的输出函数,"
    "比endl要快
                    break;
                }
            }
        }
    }

    直接交表的代码(确实不太能手算,我错了

    #include<bits/stdc++.h>
    using namespace std;
    int num[]= {0, 4, 22, 27, 58, 85, 94, 121, 166, 202, 265, 274, 319, 346, 355, 378, 382, 391, 438, 454, 483, 517, 526, 535, 562, 576, 588, 627, 634, 636, 645, 648, 654, 663, 666, 690, 706, 728, 729, 762, 778, 825, 852, 861, 895, 913, 915, 922, 958, 985, 1086, 1111, 1165};
    int main()
    {
        int a;
        while(~scanf("%d",&a))
        {
            printf("%d
    ",num[a]);
        }
        return 0;
    }

    5618: Kannyi爱干净 分享至QQ空间

    时间限制(普通/Java):1000MS/3000MS     内存限制:65536KByte
    总提交: 272            测试通过:91

    描述

     

    Kannyi是一个非常爱干净的男孩子,他喜欢把自己东西都归回原位。

    今天,Kannyi面对了一个问题,他要把他的袜子放进衣柜里。Kannyi的收纳包里有n双已经标过编号的袜子(每双袜子一对相同编号,从1到n),现在袜子乱了,他想把它们成双放进衣柜里。他会随机从他的包里拿出来一只袜子,与此同时他寻找着桌上有没有一只袜子可以和拿出来的袜子配对。如果可以,他就会把它们一起放进衣柜里,否则他就把这只袜子放在桌子上。就这样,他把所有的袜子都整齐地放进了衣柜里。

    现在Kannyi给你了他从收纳包取袜子的号码序列,请你告诉他桌子上出现过的最多袜子只数。

    输入

     

    输入数据包含多组测试实例,每个测试实例占两行。

    第一行一个数n,表示Kannyi的袜子双数(1≤n≤10)。

    第二行一共2n个整数,表示了Kannyi从收纳包依次取得的袜子号码a1,a2,...,a2n(1≤ai≤n)。

    输出

     

    每组样例输出收拾过程中桌子上出现过的最多袜子只数。

    样例输入

    1
    1 1
    3
    2 1 1 3 2 3

    样例输出

    1

    2

    这个题目就是照抄Codeforces的A,但是原题要用hash方法,你们暂时没有学就标记变量就好啦

    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        int n;
        while(cin>>n)
        {
            int a[15]={0},ma=0;
            for(int i=0,x; i<2*n; i++)
            {
                cin>>x;
                a[x]++;
                int f=0;
                for(int i=1;i<=n;i++)
                    if(a[i]==1)f++;
                ma=max(ma,f);
            }
            cout<<ma<<"
    ";
        }
        return 0;
    }

    如果用set那就比较简单了,stl的学习可以到这篇博客

    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        int n;
        while(cin>>n)
        {
            n*=2;
            set<int>S;
            int ma=0;
            for(int i=0,x; i<n; i++)
            {
                scanf("%d",&x);
                if(S.count(x))S.erase(x);
                else S.insert(x);
                ma=max((int)S.size(),ma);
            }
            cout<<ma<<"
    ";
        }
        return 0;
    }

    5620: Kannyi的正方体和圆柱体 分享至QQ空间

    时间限制(普通/Java):1000MS/3000MS     内存限制:65536KByte
    总提交: 447            测试通过:175

    描述

     

    签到是不可能签到的,这辈子都不可能不签到的。

    Kannyi现在有一个正方体和一个圆柱体,且两个是等高的,并且侧面积是相等。Kannyi很快知道了正方体的边长a,请聪明的你告诉他圆柱体的体积。

    输入

     

    输入数据包含多个测试实例,每个测试实例占一行,为一整数a(1 ≤ a ≤ 102)

    输出

     

    每行输出为圆柱体的体积V,保留1位小数。

    样例输入

    1

    样例输出

     1.3

    提示

    PI=acos(-1)

    这个纯粹就是偷懒题,直接从高考卷拿了个选择题,不过高考卷是让求两者的比例的

    相信同学们都能解出来正方体V:圆柱V=PI:4,好心提示了一波PI的较为精确值

    #include<bits/stdc++.h>
    using namespace std;
    const double PI=acos(-1);
    int main()
    {
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        int n;
        while(cin>>n)printf("%.1f
    ",n*n*n*4/PI);
    }

    当然你也可以这样,这个题目并不卡精度

    #include<bits/stdc++.h>
    #define PI acos(-1)
    using namespace std;
    int main()
    {
        int a;
        while(cin>>a)
        {
            double sc=a*a*4.0;
            double d=sc*1.0/a; 
            double r=d/2.0/PI;
            double v=r*r*PI*a;
            printf("%.1f
    ",v);
        }
        return 0;
    } 

    5627: kannyi的独木桥 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 24            Accepted:9

    Description

     

    kannyi的女神小M经常对kannyi说,你走你的独木桥(划重点),我走我的阳关道。kannyi不以为然,却对独木桥产生了浓厚的兴趣,并产生了一个问题:

    独木桥的长度为m(1~m自西向东),每个人都有初始位置和初始方向,每个人每秒只能移动1个单位,当一个人到0或者m+1,则代表已经离开独木桥。每个人都会朝一个方向行走,中途不会自己改变方向。要是两人相遇,两人会分别转身继续行走 ,转身不需要时间。

    现在kannyi想知道最早和最迟离开独木桥的人的时间。

    Input

     

    多组输入,第一行输入整数n,m,分别代表独木桥上的人数,桥的长度。(1<=n<=m<=1000000)

    接下来n行,每行输入两个整数pi(1<=pi<=m)和di,分别代表第i个人的位置和朝向(di为-1时,朝向西,di为1时,朝向东),每个人的位置都不一样。

    保证输入数据合法。

    Output

     

    输出一行,两个整数,分别为最早和最迟离开独木桥的人的时间(秒)。两个整数由一个空格符分开。

    Sample Input

    2 4
    1 1
    3 -1

    Sample Output

    3 4

    Hint

    样例解释:第一个人在1的位置,方向向东,第二个人在3的位置,方向向西,两人在位置2相遇,然后分别转向,第一个人离开独木桥所用时间就是1->2,2->1,1->0,用时3秒;第二个人离开独木桥所用时间就是3->2,2->3,3->4,4->5,用时4秒。

     两个人相遇,可以认为他们穿过了对方,比如一个人的初始位置是1,方向向东,2秒后一定有个人在3这个位置,虽然可能不是同一个人,把全部人看做一个整体,不管是不是同一个人,对结果并没有影响,所以相遇就等价于穿过对方。 

    /*

    我的口胡

    但是题目保证数据合法了,问题就是相遇了怎么办,其实相遇了就是两者的时间的互换,自己可以试一下,因为每个人都要走到目标点的

    所以朝向东就是自己走或者相遇者走m+1-a,朝向西就是自己走或者相遇者走a,分别取大取小就是答案

    */

    #include<bits/stdc++.h>
    using namespace std;
    
    int main()
    {
        int n,m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            int minn=1e9,maxx=0;
            for(int i=0;i<n;i++)
            {
                int a,b;
                scanf("%d%d",&a,&b);
                minn=min(minn,b==1?m-a+1:a);
                maxx=max(maxx,b==1?m-a+1:a);
            }
            printf("%d %d
    ",minn,maxx);
        }
        return 0;
    }

    5621: Kannyi的简单检查 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 633            Accepted:149

    Description

     

    这里是2018年电信学院第一届新生程序设计竞赛 ,当然要和1有关了。大家都非常喜欢数字1,不管在游戏中还是现实中,都有个冠军梦。IG niubi!

    Kannyi现在给你一个数字a,让你看看是否只含数字1,如果是请输出让人心动的"Accepted",不是请输出令人心碎的"Wrong Answer"。

    Input

     

    输入数据包含多个测试实例,每个测试实例占一行,为整数a(-109≤ a ≤ 109)

    Output

     

    每个测试样例输出为1行,输出"Accepted"或"Wrong Answer"。

    Sample Input

    1
    10
    1111
    2333

    Sample Output

    Accepted
    Wrong Answer
    Accepted
    Wrong Answer

    注意负数就好了,简单循环,当然你也可以进制转换

    #include<bits/stdc++.h>
    using namespace std;
    int la(int x)
    {
        while(x)
        {
            if(abs(x%10)!=1)return 1;
            x/=10;
        }
        return 0;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        int x;
        while(~scanf("%d",&x))
        {
            if(la(x))printf("Wrong Answer
    ");
            else printf("Accepted
    ");
        }
    }

    循环就完事的

    #include<bits/stdc++.h>
    using namespace std;
    const double PI=acos(-1);
    int main()
    {
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        char s[15];
        while(~scanf("%s",s))
        {
            int f=0;
            for(int i=0; s[i]; i++)
            {
                if(s[i]=='-')continue;
                else if(s[i]!='1')f=1;
            }
            if(f)printf("Wrong Answer
    ");
            else printf("Accepted
    ");
        }
    }

    5622: Kannyi的倒计时 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 310            Accepted:160

    Description

     

    本次新生赛在12-08举办,工作人员需要执行出题、借教室等各种各样任务,但是这一群人有人有拖延症。

    有句话怎么讲呢,deadline才是第一生产力,只要有某件事你看到需要迫切去做了,你就会去做。所以Kannyi要通过倒计时去刺激这些工作人员。

    本次活动时间对于工作人员来说是11-12到12-10,所以请你算算给定工作时间距今天(12-08)有几天,为了区分在比赛前还是比赛后,请加上"+"、"-"号以便区别。

    Input

     

    输入数据包含多个测试实例,每个测试实例占一行,为一日期,格式为: "MM-DD"。

    Output

     

    每行输出为距离今天的天数x。

    Sample Input

    11-12
    12-08
    12-10

    Sample Output

    +26
    0
    -2

    不要说没给a+b签到哦,算日期是典型的a+b(强行a+b

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        int m,d;
        while(~scanf("%d-%d",&m,&d))
        {
            int ans;
            if(m>11)ans=8-d;
            else ans=38-d;
            if(ans>0)printf("+");
            printf("%d
    ",ans);
        }
    }

    5628: kannyi的开矿规划 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 14            Accepted:1

    Description

     

    kannyi是某外星开矿公司的负责人,打算在某星球开矿。已知,这星球上的矿井都有对应的坐标点。一切开矿作业都靠开矿bot而非人类完成,飞船上只有一个传送门,请你思考一下在这颗星球上最多能获利多少。

    *对传送门能做的所有操作:

        1.启动:启动传送门需要消耗E1的电力,不消耗时间。

        2.关闭:关闭传送门不消耗电力和时间。

        3.修改:修改传送门的目标,不消耗电力和时间。但在传送门关闭的时候才能修改。修改的目标只能是每个矿井的入口处。

        4.传送:当传送目标确定后并且传送门开启的时候,可以选择将任意数量的开矿bot传送到指定地点。这不消耗时间但是会消耗n*E2的电力(n为传送的bot的数量),而将n个bot传送回来消耗的电力也相同。

        5.维持:只要传送门启动着,每过一个单位时间就需要消耗E3的电力。

    *对bot能做的操作:

        1.移动:无论是上移或是下移,bot都会消耗1电力和1单位时间(电力充足无须担心)。从矿井入口下到地下单位一层也视作移动,从传送门到矿井入口不视作移动。

        2.开采&装载:一个bot理论能承担至多25单位的矿物,开采25单位矿物消耗1电力和2单位时间(开采操作默认会直接挖够25矿物),无论携带多少矿物都不影响bot的移动速度和消耗电力。

        3.等待:等待不消耗电力。

    *杂项:

        1.1电消耗1钱,1矿换1钱,不存在电费梯度和矿物过度导致市场饱和问题。

        2.中途赤字不影响开工。

        3.传送门只有在指向某个坐标的时候,这个坐标的bot才能够传送回去。

        4.消耗电力是每个bot单独计算,而时间层面,各个bot行动可以同时进行。

        5.第n层没有挖掘不影响bot直接下到n层下面的移动动作。

    Input

     

    多组输入,每组的第一行是一个正整数N(1<=N<=100),代表该星球上已经布置了多少坐标点,输入以N=0结束。

    然后是三个正整数E1,E2和E3(1<=E2,E3<E1<=50)。

    接下来是N对正整数ti和hi,ti表示该传送点下的矿井单位深度内有多少矿物,hi表示该矿井极限深度。(0<=ti<=500,ti%25=0,1<=hi<=10)。

    Output

     

    输出最多盈利额。

    Sample Input

     

    Sample Output

     

    Hint

    开门后传送5个bot过去,分别派遣到1-5层,挖掘结束后回到传送门回来。

    bot共消耗35电量,花费总时间12单位,传送门则消耗15+1*12(等待)=27电量,实际获取矿物125单位。

    所以最终盈利为53。

    这个题目看懂题意,模拟一下就好了(看不懂或者感觉卡就对了,但是绝对是能做的

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n,E1,E2,E3,t,h;
        while(~scanf("%d",&n),n){
            scanf("%d%d%d",&E1,&E2,&E3);
            int ans=0;
            while(n--){
                scanf("%d%d",&t,&h);
                int maxx=0,el=0;
                for(int i=1;i<=h;i++){
                    el+=(2*i+1)*t/25;
                    maxx=max(maxx,i*t-(min(2*(i+1)*E3,E1)+el+t*i/25*2*E2+E1));
                }
                ans+=maxx;
            }
            printf("%d
    ",ans);
        }
        return 0;
    }

    5624: Kannyi的闯关游戏 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 39            Accepted:15

    Description

     

    Kannyi最近迷上了一款通关的小游戏,是魔塔的简化版,现在继续将这个简化。

    有一二维的图像,你可以在一个时间单位内进行上下左右移动,要通往下一关需要先去拿到钥匙,再去门口,是有时间限制的。现在Kannyi把图告诉你,希望你能告诉他最快可以在几个时间单位内到达,如果不能到达请输出"Oops! Something went wrong"(不包含引号)。

    Input

     

    题目包括多组测试数据。 

    每组测试数据以两个整数n,m(0<n, m≤20)开头,分别代表图像的长和高。紧接着有n行,m列字符,由".","#","S","K","E"组成。其中:

    "." 代表能够行走的空地。 
    "#" 代表墙壁,人物不能从此通过,除此之外均可以通过。 
    "S" 是人物初始所在的位置。 

    "K" 是钥匙所在的位置。 

    "E" 是通往下一关的门的位置。
    任务只能选择上、下、左、右任意一方向走一步。 

    Output

     

    每组输出Kanny通过这一关的最短时间,如果不能达到输出"Oops! Something went wrong"。

    Sample Input

    1 3
    SEK
    4 4
    ....
    ....
    ..K.
    S##E
    4 4
    ....
    ...K
    .###
    S##E

    Sample Output

    3
    5
    Oops! Something went wrong

    emmm,只有bfs可以找到最短路,dfs还是不要费力气了

    我有了一个C++11的写法,打开C++11用这个

    #include<bits/stdc++.h>
    using namespace std;
    struct T
    {
        int x,y,f;
    }q[405];
    int d[4][2]={1,0,-1,0,0,1,0,-1};
    char s[25][25];
    bool vis[25][25];
    int n,m,kx,ky;
    int bfs(int x,int y)
    {
        memset(vis,0,sizeof vis);
        int tot=0,sum=1;
        q[0]={x,y,0};//这个是c++11才支持的,其他版本你一个一个赋值就好
        vis[x][y]=1;
        while(tot<sum)
        {
            for(int i=0;i<4;i++)
            {
                int nx=q[tot].x+d[i][0],ny=q[tot].y+d[i][1];
                if(nx==kx&&ny==ky)return q[tot].f+1;
                if(nx>=0&&ny>=0&&nx<n&&ny<m&&vis[nx][ny]==0&&s[nx][ny]!='#')
                vis[nx][ny]=1,q[sum++]={nx,ny,q[tot].f+1};
            }
            ++tot;
        }
        return 1000;
    }
    int main()
    {
        //freopen("1.in","r",stdin);
        //freopen("1.out","w",stdout);
        while(~scanf("%d%d",&n,&m))
        {
            for(int i=0; i<n; i++)scanf("%s",s[i]);
            int sx,sy,ex,ey;
            for(int i=0; i<n; i++)
                for(int j=0; j<m; j++)
                {
                    if(s[i][j]=='K')kx=i,ky=j;
                    else if(s[i][j]=='S')sx=i,sy=j;
                    else if(s[i][j]=='E')ex=i,ey=j;
                }
            int ans=bfs(sx,sy)+bfs(ex,ey);
            if(ans>1000)printf("Oops! Something went wrong
    ");
            else printf("%d
    ",ans);
        }
    }

    5625: Kannyi爱种树 分享至QQ空间

    Time Limit(Common/Java):2000MS/6000MS     Memory Limit:65536KByte
    Total Submit: 258            Accepted:6

    Description

     

    在一堂有趣的C语言实验课上,老师让同学们做一道题叫校门外的树,这时一个同学问我,这个题还有这么一个思路,(以下省略AC答案)。我一听,小伙子不错,这想法很奇妙,为了满足他的小需求(变态需求),我特地为他出了这么一个题。

    Kannyi是一个植树队的队长,为了让城市变得更美好,他决定在城市的马路上种上树,于是他向政府提出了申请,政府很快就通过了,并给了一些要求。政府要求Kannyi只允许在马路一侧种上树,并给他划出了一段总长为L米的马路。政府还划定了M段连续的区域需要种上树。

    Kannyi一看这么多要求头就大了,政府划定的区域有长有短,有覆盖还有包含,这让他不知如何是好,愚蠢的他算了好几天也没算清楚到底要种几棵树,于是他找到了聪明的你,希望你能帮助他解决这个问题。

    Input

     

    第一行输入一个T代表一共T组数据,每组数据输入第一行包含两个非负整数L和M,L代表马路的总长度,M代表需要种树的区域。接下来M行,每行有两个数字a,b,代表区域[min(a,b),max(a,b)]需要种上树。

    数据保证:

    T≈100

    1<=L<=10^6

    0<=M<=10^5

    1<=a,b<=L

    Output

     

    输出包含一行,表示Kannyi的植树队满足政府的所有需求后到底需要在马路上种多少颗树。

    Sample Input

    2
    10 4
    4 4
    3 7
    1 3
    2 5
    5 0

    Sample Output

    7
    0

    Hint

    样例1:[1,7]被种上了树,一共种了7棵树。

    样例2:没有地方被种上树。

    这个题目数据范围非常之大,暴力标记什么的都是不行的,我们要使用线段求交,实质就是解决三种问题

    ————————

                                      ————————

    ————————

                     ———————

    ————————
          ————

    然后就可以愉快做题了

    我的代码,等一波他们简单易懂的代码

    #include<bits/stdc++.h>
    using namespace std;
    vector<pair<int,int> >V;
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            V.clear();
            int n;
            scanf("%d%d",&n,&n);
            for(int i=0,x,y; i<n; i++)scanf("%d%d",&x,&y),V.push_back({min(x,y),max(x,y)});
            sort(V.begin(),V.end());
            int r=-1,s=0;
            for(auto X:V)
            {
                if(X.second<=r)continue;
                if(X.first>r) s+=X.second-X.first+1;
                else s+=X.second-r;
                r=X.second;
            }
            printf("%d
    ",s);
        }
    }

    5626: Kannyi 的easy problem 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 703            Accepted:103

    Description

     

    已知式子2n – 1,求该式子在1到n范围内能被7整除的正整数n的个数有多少个?

    Input

     

    多组输入,每组输入只有一个n(0<n<=1e18),当n等于0时程序结束。

    Output

     

    输出范围内n的个数。

    Sample Input

    10
    0

    Sample Output

    3

    猜就完事,猜对了做不出来看看A的提示啊

    这个题的证明可以这样来,2^n-1之后就是一个全为1的2进制数列

    然后我进行求余,发现三位就是一循环(其实这就是编译原理的自动机

    桃子的归纳法

    #include<stdio.h>
    int main()
    {
        __int64 n;
        while(scanf("%I64d",&n),n)printf("%I64d
    ",n/3);
    }

    5629: Kannyi的复旦 分享至QQ空间

    Time Limit(Common/Java):3000MS/6000MS     Memory Limit:80000KByte
    Total Submit: 103            Accepted:11

    Description

     

    校园的天色逐渐暗了下来,教学楼里的人们也匆匆散去。Kannyi为了实现他小小的名校梦想,孑身一人坐在学习室里静修。不知不觉之中,时间过了零点。Kannyi的女神小M见状,便上前劝着Kannyi早些休息。然而Kannyi却依然沉迷在自己的考研真题中,丝毫没有困意。

    于是,小M给Kannyi出了一道难题,如果Kannyi答错,就必须答应小M回去休息。但是Kannyi可不会轻易认输,便答应了小M的挑战:

    小M给出了四个数列A,B,C,D,每个数列都包含着n个数。需要Kannyi从每个数列中各取出1个数,使4个数之和为0,并求出这样的组合个数。当一个数列中有多个相同数字时,把它们作为不同的数字看待。

    Kannyi真的很想考上复旦大学^-^,所以他必须留下来继续学习,你能帮一帮为梦想执着的Kannyi嘛>o<!

    Input

     

    第一行输入n,表示各数列所含数字的个数。(1≤n≤4000)

    接下来有4行,分别代表A,B,C,D数列,每行有n个数字。(|各数字的值|≤1000)

    Output

     

    符合条件的组合总数。

    Sample Input

     6

    -45 -41 -36 -36 26 -32
    22 -27 53 30 -38 -54
    42 56 -37 -75 -10 -6
    -16 30 77 -46 62 45

    Sample Output

     5

    Hint

    样例中符合条件的5种组合如下:

    {-45 -27 42 30}

    {26 30 -10 -46}

    {-32 22 56 -46}

    {-32 30 -75 77}

    {-32 -54 56 30} 

     a+b+c+d=0,(a+b)=-(c+d) ,
     就是预处理出a+b和c+d,然后二分找-(c+d) 

    (二分或者用stl的hash unordered_map优化,打开C++11用这个

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=4005;
    ll a[N],b[N];
    unordered_map<ll,int> ma;
    int main()
    {
        int n,i,j;
        scanf("%d",&n);
        for(i=0;i<n;++i) scanf("%lld",&a[i]);
        for(i=0;i<n;++i) scanf("%lld",&b[i]);
        for(i=0;i<n;++i)
            for(j=0;j<n;++j)
                ++ma[a[i]+b[j]];
        for(i=0;i<n;++i) scanf("%lld",&a[i]);
        for(i=0;i<n;++i) scanf("%lld",&b[i]);
        ll ans=0;
        for(i=0;i<n;++i)
            for(j=0;j<n;++j)
                ans+=ma[-a[i]-b[j]];
        printf("%lld
    ",ans);
        return 0;
    }

     出题人的代码

    #include<cstdio> 
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define MAX 4005
    int a[MAX],b[MAX],c[MAX],d[MAX];
    int cd[MAX*MAX];
    int main()
    {
        //freopen("test.in","r",stdin);
        //freopen("test.out","w",stdout); 
        int n,i,j;
        cin>>n;
        for(i=0;i<n;i++) scanf("%d",&a[i]);
        for(i=0;i<n;i++) scanf("%d",&b[i]);
        for(i=0;i<n;i++) scanf("%d",&c[i]);
        for(i=0;i<n;i++) scanf("%d",&d[i]);
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                cd[n*i+j]=c[i]+d[j]; 
        sort(cd,cd+n*n);
        long long res=0;
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
            {
                int s=-a[i]-b[j];
                res+=upper_bound(cd,cd+n*n,s)-lower_bound(cd,cd+n*n,s);
            } 
        cout<<res<<endl;
        return 0;
    }

     对着库函数的二分实现一下,库函数的二分实现

    #include<bits/stdc++.h>
    using namespace std;
    const int m=4005;
    int n,a[m],b[m],c[m],d[m],cd[m*m];
    int low(int key)
    {
        int step,mid,L=0,R=n*n;
        while(R>0)
        {
            step=R>>1,mid=L+step;
            key>cd[mid]?(L=mid+1,R=R-step-1):(R=step);
        }
        return L;
    }
    
    int up(int key)
    {
        int step,mid,L=0,R=n*n;
        while(R>0)
        {
            step=R>>1,mid=L+step;
            key>=cd[mid]?(L=mid+1,R=R-step-1):(R=step);
        }
        return L;
    }
    int main()
    {
        int i,j;
        long long sum=0;
        scanf("%d",&n);
        for(i=0; i<n; i++)
            scanf("%d",&a[i]);
        for(i=0; i<n; i++)
            scanf("%d",&b[i]);
        for(i=0; i<n; i++)
            scanf("%d",&c[i]);
        for(i=0; i<n; i++)
            scanf("%d",&d[i]);
        for(i=0; i<n; i++)
            for(j=0; j<n; j++)
                cd[i*n+j]=c[i]+d[j];
        sort(cd,cd+n*n);
        for(i=0; i<n; i++)
            for(j=0; j<n; j++)
            {
                int s=-a[i]-b[j];
                sum+=up(s)-low(s);
            }
        cout<<sum;
        return 0;
    }

     没学会二分的这里看 

    #include<stdio.h>
    #include<algorithm>
    #define m 4005
    int n,a[m],b[m],c[m],d[m],cd[m*m];
    int low(int t)
    {
        int l=0,r=n*n;
        while(l<r)
        {
            int mi=l+(r-l)/2;
            if(cd[mi]<t)l=mi+1;
            else r=mi;
        }
        return l;
    }
    int up(int t)
    {
        int l=0,r=n*n;
        while(l<r)
        {
            int mi=l+(r-l)/2;
            if(cd[mi]<=t)l=mi+1;
            else r=mi;
        }
        return l;
    }
    int main()
    {
        int i,j;
        long long sum=0;
        scanf("%d",&n);
        for(i=0; i<n; i++)
            scanf("%d",&a[i]);
        for(i=0; i<n; i++)
            scanf("%d",&b[i]);
        for(i=0; i<n; i++)
            scanf("%d",&c[i]);
        for(i=0; i<n; i++)
            scanf("%d",&d[i]);
        for(i=0; i<n; i++)
            for(j=0; j<n; j++)
                cd[i*n+j]=c[i]+d[j];
        std::sort(cd,cd+n*n);
        for(i=0; i<n; i++)
            for(j=0; j<n; j++)
            {
                int s=-a[i]-b[j];
                sum+=up(s)-low(s);
            }
        printf("%I64d",sum);
        return 0;
    }
    简单的实现,有点慢

    利用绝对值很小去hash,solution from zhouzexi

    #include <bits/stdc++.h>
    using namespace std;
    unordered_map<int,int>mp[5],mo;
    int main(){
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        int n,x;
        int T=4;
        cin>>n;
        while(T--)
            for(int i=0;i<n;i++){
                cin>>x;
                mp[T][x]++;
            }
        unordered_map<int,int>::iterator it,it2;
        for(it=mp[0].begin();it!=mp[0].end();it++)
            for(it2=mp[1].begin();it2!=mp[1].end();it2++)
                    mo[it->first+it2->first]+=it->second*it2->second;
        __int64 s=0;
        for(it=mp[2].begin();it!=mp[2].end();it++)
            for(it2=mp[3].begin();it2!=mp[3].end();it2++)
                    s+=1LL*mo[-(it->first+it2->first)]*it->second*it2->second;
        cout<<s<<endl;
    }

    数组的hash,可以0ms

    转载于:https://www.cnblogs.com/BobHuang/p/10090218.html

  • 相关阅读:
    3-12 初识JWT
    3-11 用户登录信息验证实现
    3-10 公共模块统一测试
    3-9 公共业务异常统一处理
    3-8 公共返回对象封装
    3-7 公共请求对象封装及优雅验证数据实现
    3-6 MD5介绍及用户添加功能实现
    3-5 用户模块数据层构建
    3-4 用户模块构建
    3-3 公共工具模块构建
  • 原文地址:https://www.cnblogs.com/twodog/p/12135473.html
Copyright © 2011-2022 走看看