1. 程式人生 > >NOIP2017普及組複賽解題報告

NOIP2017普及組複賽解題報告

這次考試在衢州二中,這裡地形太複雜了,離開考前3分鐘才找到考場。無語·········所以一開考就沒有好心情。這次的題目相對來以往的前幾道題比較簡單,但最後一道題比較難,考場上只想出來了50分的程式碼。

時間限制:1s 記憶體限制:256M

第一題:成績 score

題目
AC記錄

超級水題,NOIP史上最簡單的題沒有之一,只要c++如果入門的都會做,普通的運算子操作
難度:☆☆☆☆☆ (入門難度)
民間資料:100分
官方得分:100分

#include<bits/stdc++.h>
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<bits/stdc++.h>
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<bits/stdc++.h>
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<bits/stdc++.h>
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 (sum<k)
    {
        printf ("-1");
        return 0;
    }
    int l=0,r=1000000010;
    while (l<r)
    {
        int mid=(l+r)/2;
        if (check (mid))
        {
            r=mid-1;
            ans=mid;
        }
        else
            l=mid+1;
    }
    printf ("%d",ans);
    return 0;
}

50分程式碼

#include<bits/stdc++.h>
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 (sum<k)
    {
        printf ("-1");
        return 0;
    }
    int l=0,r=1000000010;
    while (l<r)
    {
        int mid=(l+r)/2;
        if (check (mid))
            r=mid;
        else
            l=mid+1;
    }
    printf ("%d",l);
    return 0;
}

AC程式碼 單調佇列

#include<bits/stdc++.h>
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 (;cur<i&&dis[cur]<=dis[i]-low;cur++)
        {
            while (head<=tail&&q[tail].v<f[cur])
                tail--;
            if (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 (maxx<k)
    {
        printf ("-1");
        return 0;
    }
    int l=1,r=dis[n];
    while (l<r)
    {
        int mid=(l+r)/2;
        if (check (mid))
            r=mid;
        else
            l=mid+1;
    }
    printf ("%d",l);
    return 0;
}