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

  • 相关阅读:
    zookeeper使用场景
    zookeeper安装配置
    hadoop 远程调试
    deep learning笔记
    Sentiment Analysis(1)-Dependency Tree-based Sentiment Classification using CRFs with Hidden Variables
    PRML阅读笔记 introduction
    Python 学习笔记(2)
    python nltk 学习笔记(5) Learning to Classify Text
    python nltk 学习笔记(4) Writing Structured Programs
    python nltk 学习笔记(3) processing raw text
  • 原文地址:https://www.cnblogs.com/twodog/p/12135472.html
Copyright © 2011-2022 走看看