SNNU2017校赛(部分)题解

来源:互联网 发布:蘑菇软件是什么 编辑:程序博客网 时间:2024/06/06 01:53

1.热身赛B题:

一开始,就用map存了下每个数字出现的次数,然后扫一遍,碰见1就输出(WA的不知所措)中午,跟西电大佬吃饭的时候,发现他也是用的map,然后iterator扫一遍过了,用队列的我表示从来没这么玩过(大佬下午就全场第三orz);然后SNNU大佬告诉我标程。。xor的逆运算是xor,输入的时候和0异或就好,最后剩下的就是答案,我竟然忘了(ys我对不起你),但是依然不知道,第一发为什么错。。有个学长说他用map也没过,换set就好了。。。(ys告诉我这水题,尽量别用stl。。。然后下午A题崩了用了一堆stl无限WA)

2.正式赛(引用大佬的)

A:开个数组score[i]保存A-Z的分数,最后输出最高的,注意分数相同字典序,(如此水题我竟然1个半小时卡了。。全场过的人辣么多,死在热身赛的stl上了,水题水做)

C:三进制计算器:3进制阶乘统计末尾0,现场·我是打表3^k,然后可以整除就加对应的k也算是过了,不过标程很强
证明:一个数N!=M*3^k,k就是0的个数,易知,当K最大时M3互质,所以我们只要分离出N!中所有的3即可。也就是说其实计算N! = m*3^kK的最大值,只要不断将n/3的结果加到K中去就好,每次执行一次操作n=n/3.显示意义就是计算连续的N个数中3的倍数,9的倍数,27的倍数......所有之和,同时在每次计算时3i次方被恰好会被计算i次,从而保证程序的正确性。
#include <iostream>#include <ctime>#include <cmath>#include <cstdio>#include <cstring>#include <vector>#include <cstdlib> using namespace std; int main(){    int t;    scanf("%d",&t);    long long n;    while(t--)    {        long long sum=0;        scanf("%lld",&n);        while(n!=0)        {            sum+=n/3;            n=n/3;        }        printf("%lld\n",sum);    }    return 0;}

D:题目要求N个点中距离距离最近的两个点的距离,看似是一道平面上N个点求最短距离的KD树题目(我就是这么想的,一开始果断放),但是仔细观察题目中所给的距离D的定义D = |(X2-X1)+(Y2-Y1)| 可做如下变形:D = |(x2+y2)-(x1+y1)|;(最后,好像10几分钟的时候,瞟了两眼,想死啊,幸好发现了)

E:这题弱连输入都不会,感觉gets()有问题,下来学习了一下,学到了新函数,我这string类和输入输出看样子得大补;
解法:每次如果行末有";"就增加缩进,有空行就减少缩进;比赛的时候,好像因为生成数据的系统和评测机的不一样,要在行末多加个空行。
#include <iostream>#include <string>#include <cstdio>using namespace std;string s;int tab;int main(){    //freopen("input.txt","r",stdin);    //freopen("output.txt","w",stdout);    tab=0;    bool flag=0;    while(getline(cin,s))//getline    {        if(s.empty())        {            for(int i=0;i<tab;i++)            cout<<" ";            cout<<endl;            tab-=4;            continue;        }        if(s[s.length()-1]==':')            flag=1;        for(int i=0;i<s.length();i++)        {            if(s[i]==',')            {                s.insert(i+1," ");                i++;            }        }        for(int i=0;i<tab;i++)            cout<<" ";        cout<<s<<endl;        if(flag)        {            tab+=4;            flag=0;        }    }    return 0;}
F:这类状态压缩问题之前一直没做过,一开始的思路是:dp[i][3][3][3][3][3][3][3][3][3][3][m]表示状态i指的是前i件物品,后面的10维0表示偶数,1表示奇数,2表示这一维未被使用,m表示魅力值,显然魅力m<=100000,加上前面的维,数组开不了。然后,又想可以常规降维
dp[i][3][3][3][3][3][3][3][3][3][3]=m;这显然也挺麻烦的,最后看了标程,dp[i][W]=m,W是用2进制的形式存下当前状态,解决了维度不确定的问题,还有数组开不下的问题,最强的是又利用了一次xor的性质,
W=W^X,每次xor表示这一位出现次数的奇偶,相当于将01背包中的体积转化为奇偶状态,dp[0][0]=0;其余为-1,表示不能直接到达
#include<bits/stdc++.h>using namespace std;int bag[1003][1111];int main(){    //freopen("test.in","r",stdin);    //freopen("test.out","w",stdout);    int T;    cin>>T;    while(T--)    {        int n,m;        cin>>n>>m;        int goal=1<<m;        memset(bag,-1,sizeof(bag));        bag[0][0]=0;        for(int i=1 ; i<=n ; i++)        {            int q,s;            cin>>q>>s;            int w=0;            for(int j=0; j<s; j++)            {                int c;                cin>>c;                w=w|(1<<(c-1));//压缩成二进制***            }            for(int j=0; j<goal; j++)            {                if(bag[i-1][j]==-1)                    continue;                bag[i][j^w]=max(bag[i][j^w],bag[i-1][j]+q);                bag[i][j]=max(bag[i][j],bag[i-1][j]);            }        }        cout<<bag[n][goal-1]<<endl;    }    return 0;}


H:1)对于有i个数字的状态dp[i]可以由(i-1,n-i+1)...(1,n-1),满足乘法原理。1-33打表即可。
2)卡特兰数法:dp[n]=h[n-1];h[n]=C(2n,n)/(n+1)=C(2n,n)-2C(2n,n-1)(n=0,1,2,...)
I:2进制大数加法,乘法,记下模板:
#include<bits/stdc++.h>using namespace std;char a[2003];char b[2003];char c[2003]; void toadd(char sa[],char sb[],int len){    for(int i=0; i<len; i++)    {        if((sa[i]&sb[i])=='1')        {            for(int j=i; j<=len; j++)            {                if(sa[j]=='1')                {                    sa[j]='0';                }                else                {                    sa[j]='1';                    break;                }            }        }        else        {            sa[i]=max(sa[i],sb[i]);        }    }}   int main(){    int T;    cin>>T;    getchar();    while(T--)    {        memset(a,0,sizeof(a));        memset(b,0,sizeof(b));        int la=0;        int lb=0;        char ch;        int opr;        while(1)        {            ch=getchar();            if(ch=='*')            {                opr=1;                break;            }            else if(ch=='+')            {                opr=0;                break;            }            a[la++]=ch;        }        while(1)        {            ch=getchar();            if(ch=='\n')                break;            b[lb++]=ch;        }        for(int i=0 ; i<la/2 ; i++)        {            int t=a[i];            a[i]=a[la-i-1];            a[la-i-1]=t;        }        for(int i=0 ; i<lb/2 ; i++)        {            int t=b[i];            b[i]=b[lb-i-1];            b[lb-i-1]=t;        }        if(opr==0)        {            toadd(a,b,max(la,lb));        }        else        {            for(int i=0 ; i<2002 ; i++)                c[i]='0';            for(int i=0,j=lb-1; i<lb; i++,j--)            {                if(b[i]=='1')                {                    toadd(c+i,a,la+i);                }            }            strcpy(a,c);        }        la=0;        for(int i=2002 ; i>0 ; i--)        {            if(a[i]>'0')            {                la=i;                break;            }        }        for(int i=la; i>=0; i--)        {            cout<<a[i];        }        cout<<endl;    }    return 0;}
(难题看懂以后补上)

orz






0 0
原创粉丝点击