1. 程式人生 > 實用技巧 >NOIP往年真題刷題記錄

NOIP往年真題刷題記錄

NOIP2010

T1 機器翻譯

水題。維護個佇列記錄一下單詞有沒有被翻譯過即可。

T2 烏龜棋

與傳紙條這個題類似,直接將卡片當作DP狀態。$n^4$DP即可。

關鍵程式碼:

f[0][0][0][0]=a[1];
    for (int i=0;i<=g[1];i++)
        for (int j=0;j<=g[2];j++)
            for (int k=0;k<=g[3];k++)
                for (int l=0;l<=g[4];l++)
                {
                    int x=i+2
*j+3*k+4*l+1; if (i!=0) f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k][l]+a[x]); if (j!=0) f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k][l]+a[x]); if (k!=0) f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k-1][l]+a[x]); if (l!=0) f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k][l-1
]+a[x]); }

T3 關押罪犯

經典老題。維護兩種並查集。每次對於$(u,v)$分別向對立面連邊,維護連通性即可。

關鍵程式碼:

for (int i=1;i<=m;i++)
    {
        int r1=find(s[i].a);
        int r2=find(s[i].b);
        if (r1==r2){
            cout<<s[i].c;return 0;
        }
        f[r2]=find(s[i].a+n);
        f[r1]=find(s[i].b+n);
    }

T4 引水入城

搜尋+剪枝。對於第一問很好做,直接dfs即可;對於第二問我們要維護一個對於每個位置向左向右單調遞減的最遠位置,因為顯然將水泵建到越高的位置越好。

程式碼:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=505;
const int dx[]={-1,0,1,0};
const int dy[]={0,1,0,-1};
int vis[N][N],a[N][N],l[N][N],r[N][N];
int n,m,cnt,flag;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void dfs(int x,int y)
{
    vis[x][y]=1;
    for (int i=0;i<4;i++)
    {
        int xx=x+dx[i],yy=y+dy[i];
        if (xx<1||xx>n||yy<1||yy>m) continue;
        if (a[xx][yy]>=a[x][y]) continue;
        if (!vis[xx][yy]) dfs(xx,yy);
        l[x][y]=min(l[x][y],l[xx][yy]);
        r[x][y]=max(r[x][y],r[xx][yy]);
    }
}
int main()
{
    memset(l,0x3f,sizeof(l));
    memset(vis,0,sizeof(vis));
    memset(r,0,sizeof(r));
    n=read();m=read();
    for (int i=1;i<=m;i++)
        l[n][i]=r[n][i]=i;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            a[i][j]=read();
    for (int i=1;i<=m;i++)
        if (!vis[1][i]) dfs(1,i);
    for (int i=1;i<=m;i++)
        if (!vis[n][i]) flag=1,cnt++;
    if (flag){
        cout<<0<<endl<<cnt;
        return 0;
    }
    int x=1;
    while(x<=m){
        int maxr=0;
        for (int i=1;i<=m;i++)
            if (l[1][i]<=x) maxr=max(maxr,r[1][i]);
        cnt++;x=maxr+1;
    }
    cout<<1<<endl<<cnt;
    return 0;
}

NOIP2011

T1 鋪地毯

模擬即可。因為只是對單點查詢,直接看地毯能不能包住這個位置。

T2 選擇客棧

遞推。考慮每個$price\leq p$的咖啡店所作的貢獻。

程式碼:

#include <iostream>
#include<cstdio>
#define maxn 200005
using namespace std;
int n,k,p;
int color,price;
int last[maxn];
int sum[maxn];
int cnt[maxn];
int ans = 0;
int now;
int main(){
    cin >> n >> k >> p;
    for (int i=1;i<=n;i++){
        cin >> color >> price;
        if (price <= p)
            now = i;
        if (now >= last[color])
            sum[color] = cnt[color];
        last[color] = i;
        ans += sum[color];
        cnt[color]++;
    }
    cout << ans << endl;
    return 0;
}

T3 Mayan遊戲

搜尋題。咕咕咕……

T4 計算係數

二項式定理秒。

T5 聰明的質檢員

不難想到是二分答案+字首和。答案是個開口向上的二次函式。根據每次$s$的取值確定二分範圍即可。

關鍵程式碼:

inline bool check(int x)
{
    for (int i=1;i<=n;i++)
        sum1[i]=sum1[i-1]+(w[i]>=x),
        sum2[i]=sum2[i-1]+(w[i]>=x)*v[i];
    int res=0;
    for (int i=1;i<=m;i++)
        res+=(sum1[a[i].r]-sum1[a[i].l-1])*(sum2[a[i].r]-sum2[a[i].l-1]);
    sum=abs(s-res);
    if (res>s) return 1;
    else return 0;
}

T6 觀光公交

神仙貪心題。先預處理出不使用但其加速公交怎麼走;然後找出一段能使最多的乘客乘車時間減少的區間,使用氮氣加速$k$次。

程式碼:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=10005;
int n,m,k,d[N];
struct node
{
    int arrive,latest,off;
}s[N];
struct Node
{
    int s,t;
}a[N];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    n=read();m=read();k=read();
    for (int i=1;i<n;i++) d[i]=read();
    for (int i=1;i<=m;i++)
    {
        a[i].s=read();int t=read();a[i].t=read();
        s[t].latest=max(s[t].latest,a[i].s);
        s[a[i].t].off++;
    }
    int tim=0;
    for (int i=1;i<=n;i++)
    {
        s[i].arrive=tim;
        tim=max(tim,s[i].latest);
        tim+=d[i];
    }
    int max_num,max_pos,tmp_num;
    while(k--)
    {
        max_num=0;
        for (int i=2;i<=n;i++)
        {
            if (!d[i-1]) continue;
            tmp_num=0;
            for (int j=i;j<=n;j++)
            {
                tmp_num+=s[j].off;
                if (s[j].arrive<=s[j].latest) break;
            }
            if (tmp_num>max_num)
            {
                max_num=tmp_num;
                max_pos=i;
            }
        }
        d[max_pos-1]--;
        for (int i=max_pos;i<=n;i++)
        {
            s[i].arrive--;
            if (s[i].arrive<s[i].latest) break;
        }
    }
    int ans=0;
    for (int i=1;i<=m;i++)
        ans+=s[a[i].t].arrive-a[i].s;
    printf("%d",ans);
    return 0;
}

NOIP2012

T1Vigenère 密碼

模擬即可。

T2 國王遊戲

神仙貪心題+高精。

T3 開車旅行

未完待續……