NOIP2017普及组复赛解题报告

这次考试在衢州二中,这里地形太复杂了,离开考前3分钟才找到考场。无语·········所以一开考就没有好心情。这次的题目相对来以往的前几道题比较简单,但最后一道题比较难,考场上只想出来了50分的代码。

时间限制:1s 内存限制:256M

第一题:成绩 score

题目
AC记录

超级水题,NOIP史上最简单的题没有之一,只要c++如果入门的都会做,普通的运算符操作
难度:☆☆☆☆☆ (入门难度)
民间数据:100分
官方得分:100分

#include
using namespace std;
int a,b,c;
int ans;
int main()
{
    freopen ("score.in","r",stdin);
    freopen ("score.out","w",stdout);
    scanf ("%d %d %d",&a,&b,&c);
    ans=a/5+b/10*3+c/2;
    printf ("%d",ans);
    return 0;
}

这道题对于c++的选手有一个无法避免的误差,就是使用浮点数(float || double)是直接*0.3会出现t.9999999999········的情况。e.g. :10.0*0.3=2.9999999·····
这导致一些选手出现 printf (”%d”,int (ans))使结果少一,60分

第二题:图书管理员 librarian

题目
AC记录

这道题其实也不难,暴力的时间复杂度也只有O(n*q)==O(1000000),而且图书编码与需求码都用 int 存的下,唯一的难点就是判断末尾是否相等,需要一定的代码能力

难度:★☆☆☆☆ (普及-)
难点:check 处理
民间数据:100分
官方成绩:100分

#include
using namespace std;
int n,q;
int a[1010];
int main()
{
    freopen ("librarian.in","r",stdin);
    freopen ("librarian.out","w",stdout);
    scanf ("%d%d",&n,&q);
    for (int i=1;i<=n;i++)
        scanf ("%d",&a[i]);
    sort (a+1,a+n+1);      //c++ STL
    for (int i=1;i<=q;i++)
    {
        int l,x;
        bool flag=0;
        scanf ("%d %d",&l,&x);
        int t=pow (10,l);          //计算t 即末尾0的个数
        for (int i=1;i<=n;i++)
            if ((a[i]-x)%t==0)   //重点:若x是a[i]的位数,那么(a[i]-x)%t==0
            {
                flag=1;
                printf ("%d\n",a[i]);
                break;
            }
        if (!flag)
            printf ("-1\n");
    }
    return 0;
}

这道题可以有变式,就是将字典编码长度与查询码长度最大为100(注意是长度,不是数值),这样对于排序就有点难了。

第三题:棋盘 chess

题目
AC记录

难度:★★☆☆☆ (普及刚好)
民间数据:100分
官方数据:100分

这道题代码量还是挺大的,考试时用记忆化搜索做的,但据说最好的解决方案是广度优先搜索,但我的方法也AC了,记忆化搜索相对来说好写一点但是,需要很细心,在考场上

#include
using namespace std;
int minn[110][110][2];
int n,m,ans=10000;
int a[110][110];
void DFS (int x,int y,int cos,bool way,int c)
{
    if (minn[x][y][c]<=cos)
        return;
    minn[x][y][c]=cos;
    if (x==m&&y==m)
    {
        ans=min (ans,cos);
        return;
    }
    if (c==a[x+1][y])
        DFS (x+1,y,cos,1,c);
    else if (a[x+1][y]!=-1)
        DFS (x+1,y,cos+1,1,a[x+1][y]);
    else if (way)
    {
        if (c==1)
        {
            DFS (x+1,y,cos+2,0,1);
            DFS (x+1,y,cos+3,0,0);
        }
        else
        {
            DFS (x+1,y,cos+2,0,0);
            DFS (x+1,y,cos+3,0,1);
        }
    }

    if (c==a[x-1][y])
        DFS (x-1,y,cos,1,c);
    else if (a[x-1][y]!=-1)
        DFS (x-1,y,cos+1,1,a[x-1][y]);
    else if (way)
    {
        if (c==1)
        {
            DFS (x-1,y,cos+2,0,1);
            DFS (x-1,y,cos+3,0,0);
        }
        else
        {
            DFS (x-1,y,cos+2,0,0);
            DFS (x-1,y,cos+3,0,1);
        }
    }

    if (c==a[x][y+1])
        DFS (x,y+1,cos,1,c);
    else if (a[x][y+1]!=-1)
        DFS (x,y+1,cos+1,1,a[x][y+1]);
    else if (way)
    {
        if (c==1)
        {
            DFS (x,y+1,cos+2,0,1);
            DFS (x,y+1,cos+3,0,0);
        }
        else
        {
            DFS (x,y+1,cos+2,0,0);
            DFS (x,y+1,cos+3,0,1);
        }
    }
    if (c==a[x][y-1])
        DFS (x,y-1,cos,1,c);
    else if (a[x][y-1]!=-1)
        DFS (x,y-1,cos+1,1,a[x][y-1]);
    else if (way)
    {
        if (c==1)
        {
            DFS (x,y-1,cos+2,0,1);
            DFS (x,y-1,cos+3,0,0);
        }
        else
        {
            DFS (x,y-1,cos+2,0,0);
            DFS (x,y-1,cos+3,0,1);
        }
    }
}
int main()
{
    freopen ("chess.in","r",stdin);
    freopen ("chess.out","w",stdout);
    memset (minn,-1,sizeof (minn));
    memset (a,-1,sizeof (a));
    scanf ("%d%d",&m,&n);
    for (int i=1;i<=n;i++)
    {
        int x,y,c;
        scanf ("%d%d%d",&x,&y,&c);
        a[x][y]=c;
    }
    for (int i=1;i<=m;i++)
        for (int j=1;j<=m;j++)
        {
            minn[i][j][0]=10000;
            minn[i][j][1]=10000;
        }
    DFS (1,1,0,1,a[1][1]);
    if (ans==10000)
    {
        printf ("-1");
        return 0;
    }
    printf ("%d",ans);
    return 0;
}

第四题:跳房子 jump

题目
AC记录
50分记录
20分记录

第一眼看到数据大小就想到了二分答案,然后在check函数中就想到了用动态规划(DP)来做,但这是O(N^2)的算法肯定会超时,但得50分已经可以了,但二分写错了,这是最恶心的地方,只得了20分

标准程序是用二分答案+单调队列来做的,略微超纲了,那是提高组的算法,是比较难理解的

难度:★★★★☆ (提高+)
民间数据:20分
官方得分:20分

20分代码

#include
using namespace std;
int n,d,k;
int x[500010],s[500010];
int sum=0,ans;
bool check (int u)
{
    int f[500010];
    memset (f,0,sizeof (f));
    f[0]=0;
    int ans=-1000000010;
    for (int i=0;i<=n;i++)
    {
        int l1=x[i]+d-u;
        if (l1<=x[i])
            l1=x[i]+1;
        int r1=x[i]+d+u;
        if (r1>=x[n])
            r1=x[n];
        for (int j=i+1;j<=n;j++)
            if (x[j]>=l1&&x[j]<=r1)
            {
                f[j]=max (f[j],f[i]+s[j]);
                ans=max (ans,f[j]);
            }
    }
    if (ans>=k)
        return 1;
    else
        return 0;
}
int main()
{
    //freopen ("jump.in","r",stdin);
    //freopen ("jump.out","w",stdout);
    scanf ("%d%d%d",&n,&d,&k);
    x[0]=0;
    for (int i=1;i<=n;i++)
    {
        scanf ("%d%d",&x[i],&s[i]);
        if (s[i]>0)
            sum+=s[i];
    }
    if (sumprintf ("-1");
        return 0;
    }
    int l=0,r=1000000010;
    while (lint mid=(l+r)/2;
        if (check (mid))
        {
            r=mid-1;
            ans=mid;
        }
        else
            l=mid+1;
    }
    printf ("%d",ans);
    return 0;
}

50分代码

#include
using namespace std;
int n,d,k;
int x[500010],s[500010];
int sum=0,ans;
int f[500010];
bool check (int u)
{
    memset (f,0,sizeof (f));
    f[0]=0;
    int ans=-1000000010;
    for (int i=0;i<=n;i++)
    {
        int l1=x[i]+d-u;
        if (l1<=x[i])
            l1=x[i]+1;
        int r1=x[i]+d+u;
        if (r1>=x[n])
            r1=x[n];
        for (int j=i+1;j<=n;j++)
            if (x[j]>=l1&&x[j]<=r1)
            {
                f[j]=max (f[j],f[i]+s[j]);
                ans=max (ans,f[j]);
                if (ans>=k)
                    return 1;
            }
    }
    if (ans>=k)
        return 1;
    else
        return 0;
}
int main()
{
    //freopen ("jump.in","r",stdin);
    //freopen ("jump.out","w",stdout);
    scanf ("%d%d%d",&n,&d,&k);
    x[0]=0;
    for (int i=1;i<=n;i++)
    {
        scanf ("%d%d",&x[i],&s[i]);
        if (s[i]>0)
            sum+=s[i];
    }
    if (sumprintf ("-1");
        return 0;
    }
    int l=0,r=1000000010;
    while (lint mid=(l+r)/2;
        if (check (mid))
            r=mid;
        else
            l=mid+1;
    }
    printf ("%d",l);
    return 0;
}

AC代码 单调队列

#include
using namespace std;
struct node{
    int x,v;
}q[500005];
int f[500005];
long long maxx=0;
int n,d,k;
int dis[500005],sc[500005];
bool check (int g)
{
    int low=max (1,d-g),high=d+g;
    int cur=0,head=0,tail=-1;
    memset (f,0,sizeof (f));
    for (int i=1;i<=n;i++)
    {
        for (;curwhile (head<=tail&&q[tail].vif (f[cur]!=-0x3f3f3f3f)
                q[++tail].v=f[cur],q[tail].x=dis[cur];
        }
        while (head<=tail&&dis[i]-q[head].x>high)
            head++;
        f[i]=(head<=tail)?q[head].v+sc[i]:-0x3f3f3f3f;
        if (f[i]>=k)
            return 1;
    }
    return 0;
}
int main()
{
    scanf ("%d%d%d",&n,&d,&k);
    for (int i=1;i<=n;i++)
    {
        scanf ("%d%d",&dis[i],&sc[i]);
        maxx+=max (sc[i],0);
    }
    if (maxxprintf ("-1");
        return 0;
    }
    int l=1,r=dis[n];
    while (lint mid=(l+r)/2;
        if (check (mid))
            r=mid;
        else
            l=mid+1;
    }
    printf ("%d",l);
    return 0;
}

你可能感兴趣的:(比赛)