NOIP2010提高组复赛 解题报告(C/C++)(机械翻译)(乌龟棋)(关押罪犯)(引水入城)

2017.2.18日的练习赛(NOIP2010)

作为一个OI届的晚辈,能接触到近七年前的NOIP考试无疑是一件令人兴奋的事情。这倒不是因为什么信仰,实在是因为只有一试四道题(而不是二试六道题),做起来让人没有“后顾”之忧。下面我们来看看:

1.机械翻译


解题报告:
不得不说,这道题作为第一题是非常“温柔”的。众所周知,“模拟”算法(如果能称作一个算法的话)是NOIP最常考的考点(没有之一)。这道题就是对一个数据结构“队列”进行模拟。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=1005;
int n,m,tot=0;
int head=1,tail=0,queue[N];//head头指针、tail尾指针、queue队列(用数组模拟)
int main()
{
    freopen("translate.in","r",stdin);
    freopen("translate.out","w",stdout);
    memset(queue,0,sizeof(queue));
    scanf("%d%d",&n,&m);
    while(m--)
    {
        int t;
        scanf("%d",&t);
        for(int i=head;i<=tail;i++)
        if(queue[i]==t)goto h;
        tot++;
        queue[++tail]=t;
        if(tail-head+1>n)head++;//内存占满的情况
        h:;
    }
    printf("%d",tot);
    return 0;
}

2.乌龟棋

解题报告:
这里写图片描述
看到这道题我就闻到了浓浓的动态规划的味道。众所周知,动态规划作为NOIP第二“关心”的考点,是不可能不会见到的(但是,囿于姿势水平太低,一开始是用dfs暴力做的(加上一点点剪枝))。这道题dp的思路是这样的:
先统计出走一步、两步、三步、四步的牌的个数。然后通过四个循环枚举每一种情况,这样来dp。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=355;
const int M=125;
const int P=45;
int n,m;
int f[P][P][P][P],card[5],map[N];//f数组来dp,card数组来存储四种牌的个数、map来记录每一个点的点权。
int main()
{
    memset(card,0,sizeof(card));
    freopen("tortoise.in","r",stdin);
    freopen("tortoise.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&map[i]);
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&x);
        card[x]++;
    }
    f[0][0][0][0]=map[1];//赋f数组的第一个初值。
    for(int i=0;i<=card[1];i++)
    for(int j=0;j<=card[2];j++)
    for(int k=0;k<=card[3];k++)
    for(int l=0;l<=card[4];l++)//dp过程
    {
        int loc=i+j*2+k*3+l*4+1;
        if(i)
        f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k][l]+map[loc]);
        if(j)
        f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k][l]+map[loc]);
        if(k)
        f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k-1][l]+map[loc]);
        if(l)
        f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k][l-1]+map[loc]);
    }
    printf("%d",f[card[1]][card[2]][card[3]][card[4]]);
    return 0;
}

3.关押罪犯

这里写图片描述
解题报告:
这里可以把两个罪犯是否有关联抽象成一幅图,那么我们知道,图是用临界表存储的。这里就解决了存储问题。
此外,我们还需要用到并查集的思想,记录哪两个犯人不在一个监狱里面。如果我们把临界表按路权排好序(从小到大)过后,在不断放入的过程中,要是我们找到目前的一对已经被“安排”过了,就输出。不然的话,我们就将两个分别与对方的“镜像”安排在一起。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int M=100005;
const int N=40005;
struct edge
{
    int u,w,v,next;
    edge(){next=-1; }
};
struct edge ed[M];
int num=0;
int head[N];
int father[2*N];
int n,m;
void build(int u,int v,int w)
{
    ++num;
    ed[num].u=u;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].next=head[u];
    head[u]=num;
}
bool cmp(const edge &a,const edge &b)
{
    return a.w>b.w;
}
int getfather(int x)//并查集
{
    return father[x]==x?x:getfather(father[x]);
}
int main()
{
    freopen("prison.in","r",stdin);
    freopen("prison.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        build(u,v,w);
    }
    for(int i=1;i<=2*n;i++)
    father[i]=i;
    sort(ed+1,ed+m+1,cmp);
    for(int i=1;i<=m;i++)
    {
        int u=ed[i].u,v=ed[i].v;
        if(getfather(u)==getfather(v))//“被安排”过了
        {
            printf("%d",ed[i].w);
            return 0;
        }
        father[getfather(v)]=getfather(ed[i].u+n);//“安排”一对罪犯。
        father[getfather(u)]=getfather(ed[i].v+n);
    }
    printf("0");
    return 0;
}

4.引水入城

这里写图片描述
解题报告:
这道题我们将湖泊边的每一个城市能够在沙漠边“覆盖”到的区域弄成一个线段(这个通过深搜来实现),再通过简单的小dp(或者贪心也可以)来选择哪几座靠湖的城市最优(区间dp,线段覆盖)。需要注意的是,我们为了判定能否满足要求(是否需要输出0),可以通过一个广搜来判特。

#include<string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int N=500;
int dir[5][2]={{0,1},{0,-1},{1,0},{-1,0}};
struct loc
{
    int x,y;
};
struct loc queue[N*N];
struct s
{
    int l,r;
};
struct s seg[N];
int map[N+5][N+5],f[N+5];
int flag[N+5][N+5];
int n,m;
int now,rest; 
void bfs()//广搜来判断是否能到 
{
    int head=0,tail=0;
    for(int i=1;i<=m;i++)
    {
        flag[1][i]=1;
        queue[++tail].y=i;
        queue[tail].x=1;
    }
    do
    {
        head++;
        for(int i=0;i<=3;i++)
        {
            int x=queue[head].x+dir[i][0],y=queue[head].y+dir[i][1];
            if(x>n&&x<1&&y>n&&y<1)continue;
            if(map[x][y]>map[queue[head].x][queue[head].y])continue;
            if(flag[x][y]==1)continue;
            queue[++tail].x=x;
            queue[++tail].y=y;
            flag[x][y]=1;
        }
    }while(head<tail);
}
void dfs(int x,int y)//深搜来更新能覆盖到的线段
{
    if(x==n)
    {
        seg[now].l=min(seg[now].l,y);
        seg[now].r=max(seg[now].r,y);
    }
    for(int i=0;i<=3;i++)
    {
        int x1=x+dir[i][0],y1=y+dir[i][1];
        if(x1>n&&x1<1&&y1>n&&y1<1)continue;
        if(map[x1][y1]>map[x][y])continue;
        if(flag[x1][y1]==1)continue;
        flag[x1][y1]=1;
        dfs(x1,y1);
        flag[x1][y1]=0;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",&map[i][j]);
    bfs();//判特
    for(int i=1;i<=m;i++)
    if(!flag[1][i])rest++;
    if(rest)printf("0\n%d",rest);//判特
    printf("1\n");
    for(int i=1;i<=m;i++)
    {
        memset(flag,0,sizeof(flag));
        now=i;
        seg[now].l=m+1;
        seg[now].r=0;
        flag[1][i]=1;
        dfs(1,i);
    }
    f[0]=0;
    for(int i=1;i<=m;i++)
    {
        f[i]=0x7fffffff;
        for(int j=1;j<=m;j++)
        if(i>=seg[j].l&&i<=seg[j].r)
        f[i]=min(f[i],f[seg[j].l-1]+1);
    }
    printf("%d",f[m]);
    return 0;
}

以上
2017.2.22

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2010年全国青少年信息学奥林匹克联赛(NOIP)提高复赛是一次重要的比赛。在这次比赛中,参赛选手需要展示出在信息学方面的专业知识和技能。 比赛的复赛共有两个题目,分别是算法设计题和程序设计题。算法设计题要求选手设计一个高效的算法来解决特定的问题,参赛选手需要对问题进行深入分析,找到最佳的解决方案。程序设计题要求选手根据题目要求,使用编程语言编写出能够正确解决问题的程序。 在比赛中,选手们需要在限定的时间内完成这两个题目。时间限制会对选手的应试能力和抗压能力提出要求。选手需要利用自己所学的知识和经验,灵活地运用在实际的问题中,制定出最佳的解决方案。 在复赛中,评委们会根据选手的算法设计和程序设计的质量来进行评判。他们会考察选手的算法思路是否清晰,是否能通过代码来实现自己的想法。评委们还会参考选手的编码风格,包括代码的可读性和规范性。这些综合因素将影响选手在比赛中的表现。 参加2010年NOIP提高复赛对于选手们来说是一次难得的机会。这次比赛既能测试选手们的能力和水平,也能提供一个展示自己的舞台。在这个比赛中,选手们需要充分发挥自己的优势,努力争取好的成绩,并从中积累经验,提高自己的编程能力和解决问题的能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值