NOIP 2010

Posted Grary

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP 2010相关的知识,希望对你有一定的参考价值。

tags:

  • NOIP
  • 并查集
  • 动态规划
  • 搜索
    categories:
  • 信息学竞赛
  • 总结
    机器翻译
    乌龟棋
    关押罪犯
    引水入城

    机器翻译

    Solution

      维护一个队列, 每次从词典中查词时将单词加入队列(代表内存), 当内存满的时候, 从队首弹出一个代表清空最早的单词.

    Code

#include<iostream>
#include<cstdio>
#include<queue>
#define N 1005
using namespace std;

queue<int>que;
int vis[N];

int main(){
    int n,m,q,ans=0;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&q);
        if(!vis[q]){
            if(que.size()==m){
                vis[que.front()]=false;
                que.pop();
            }
            que.push(q);
            vis[q]=1;
            ans++;
        }
    }
    printf("%d",ans);
    return 0;
}

乌龟棋

Solution

  比较原始的动态规划, \(f(i,j,k,l)\)表示使用了\(i,j,k,l\)\(1,2,3,4\)的牌, 然后依次枚举\(l,k,j,i\)来更新\(f(i,j,k,l)\).

Code

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#define N 45
using namespace std;

int dp[N][N][N][N];
int s1,s2,s3,s4;
int value[505];
int n,m,s;

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",&value[i]);
    for(int i=1;i<=m;++i){
        scanf("%d",&s);
        if(s==1)s1++;if(s==2)s2++;
        if(s==3)s3++;if(s==4)s4++;
    }
    int ans=0;
    for(int d=0;d<=s4;++d)
        for(int c=0;c<=s3;++c)
            for(int b=0;b<=s2;++b)
                for(int a=0;a<=s1;++a){
                    int siz=a+b*2+c*3+d*4;
                    if(siz>n)continue;
                    dp[a][b][c][d]+=value[siz+1];
                    int ma=dp[a][b][c][d];
                    if(a)ma=max(ma,dp[a-1][b][c][d]+value[siz+1]);
                    if(b)ma=max(ma,dp[a][b-1][c][d]+value[siz+1]);
                    if(c)ma=max(ma,dp[a][b][c-1][d]+value[siz+1]);
                    if(d)ma=max(ma,dp[a][b][c][d-1]+value[siz+1]);
                    dp[a][b][c][d]=ma;
                    // cout<<dp[a][b][c][d]<<endl;
                    if(siz+1==n)ans=max(ma,ans);
                }
    printf("%d",ans);
    // system("pause");
    return 0;
}

关押罪犯

做法

  并查集, 最开始有\(2n\)个集合, 代表每个点所属的集合和敌对的人所属的集合, 然后将所有敌对关系从大到小排序, 依次将一个点加入另一个点的敌对集合, 直到无法再加时就是最终的答案.

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#define N 515
#define inf 0x3f3f3f3f
using namespace std;

int dx[N]={0,0,-1,1};
int dy[N]={1,-1,0,0};
int h[N][N];
int be[N][N];
int also[N];
bool vis[N][N];
int n,m;
int color;
int flag=inf,en;
int visit[N];

struct Segment{
    int u,v;
    inline bool operator<(const Segment& s)const{
        if(v!=s.v)return v>s.v;return u<s.u;
    }
}e[N];
int tot;

inline bool Chu(int x,int y){
    if(x<1||y<1||x>n||y>m)return false;
    return true;
}

void dfs(int x,int y,int f){
    vis[x][y]=true;
    for(int i=0;i<4;++i){
        int xx=dx[i]+x,yy=dy[i]+y;
        if(vis[xx][yy])continue;
        if(!Chu(xx,yy))continue;
        if(h[xx][yy]>=h[x][y])continue;
        be[xx][yy]=f;
        if(xx==n)flag=min(flag,yy),en=max(en,yy);
        dfs(xx,yy,f);
    }
}

int main(){
    scanf("%d%d",&n,&m);
    if(n==1){
        cout<<"1\n4";
        return 0;
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            scanf("%d",&h[i][j]);
    // cout<<"*****************\n";
    for(int i=1;i<=m;++i){
        memset(vis,false,sizeof(vis));
        if(h[1][i-1]>h[1][i])continue;
        if(h[1][i+1]>h[1][i])continue;
        flag=inf;
        dfs(1,i,i);if(flag==inf)continue;
        e[++tot]=(Segment){flag,en};
        // cout<<flag<<' '<<en<<' '<<i<<endl;
    }
    int ans=0;
    for(int i=1;i<=m;++i){
        if(!be[n][i])++ans;
        // cout<<be[n][i]<<' ';
    }
    if(ans){
        printf("0\n%d",ans);
        return 0;
    }
    int l,r=1;int maxl,pos;ans=0;
    while(r<m+1){
        for(int i=1;i<=tot;++i){
            if(e[i].v>500||e[i].u>500)break;
            if(e[i].u<=r&&!visit[i])
                if(e[i].v>maxl){
                    pos=i,maxl=e[i].v;
                }
        }
        visit[pos]=true;
        r=maxl+1;ans++;
    }    if(!ans)ans=1;
    printf("1\n%d",ans);
    // system("pause");
    return 0;
}

引水入城

Solution

  并没有像他们说的那样会超时, 虽然使用了非常暴力的做法来处理.首先找出每个点能覆盖的处在沙漠中点的区间.然后做线段覆盖, 我处理的真的是非常随便.注意有一个数据是沙漠也是城市, 特判过; 另外如果一个临湖点比另一个一个相邻的点低, 就不需要遍历这个点了, 因为是肯定是它的子集.

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#define N 515
#define inf 0x3f3f3f3f
using namespace std;

int dx[N]={0,0,-1,1};
int dy[N]={1,-1,0,0};
int h[N][N];
int be[N][N];
int also[N];
bool vis[N][N];
int n,m;
int color;
int flag=inf,en;
int visit[N];

struct Segment{
    int u,v;
    inline bool operator<(const Segment& s)const{
        if(v!=s.v)return v>s.v;return u<s.u;
    }
}e[N];
int tot;

inline bool Chu(int x,int y){
    if(x<1||y<1||x>n||y>m)return false;
    return true;
}

void dfs(int x,int y,int f){
    vis[x][y]=true;
    for(int i=0;i<4;++i){
        int xx=dx[i]+x,yy=dy[i]+y;
        if(vis[xx][yy])continue;
        if(!Chu(xx,yy))continue;
        if(h[xx][yy]>=h[x][y])continue;
        be[xx][yy]=f;
        if(xx==n)flag=min(flag,yy),en=max(en,yy);
        dfs(xx,yy,f);
    }
}

int main(){
    scanf("%d%d",&n,&m);
    if(n==1){
        cout<<"1\n4";
        return 0;
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            scanf("%d",&h[i][j]);
    // cout<<"*****************\n";
    for(int i=1;i<=m;++i){
        memset(vis,false,sizeof(vis));
        if(h[1][i-1]>h[1][i])continue;
        if(h[1][i+1]>h[1][i])continue;
        flag=inf;
        dfs(1,i,i);if(flag==inf)continue;
        e[++tot]=(Segment){flag,en};
        // cout<<flag<<' '<<en<<' '<<i<<endl;
    }
    int ans=0;
    for(int i=1;i<=m;++i){
        if(!be[n][i])++ans;
        // cout<<be[n][i]<<' ';
    }
    if(ans){
        printf("0\n%d",ans);
        return 0;
    }
    int l,r=1;int maxl,pos;ans=0;
    while(r<m+1){
        for(int i=1;i<=tot;++i){
            if(e[i].v>500||e[i].u>500)break;
            if(e[i].u<=r&&!visit[i])
                if(e[i].v>maxl){
                    pos=i,maxl=e[i].v;
                }
        }
        visit[pos]=true;
        r=maxl+1;ans++;
    }    if(!ans)ans=1;
    printf("1\n%d",ans);
    // system("pause");
    return 0;
}

以上是关于NOIP 2010的主要内容,如果未能解决你的问题,请参考以下文章

vs 2010代码片段

vs 2010代码片段

NOIP2010

[TyvjP1313] [NOIP2010初赛]烽火传递(单调队列 + DP)

noip2010初赛模拟题

NOIP2010关押罪犯