Noip 2010 解题报告(机器翻译,乌龟棋,关押罪犯,引水入城)

首先,我只是一个刚刚接触信息学的渣渣,很多东西都还没有掌握,甚至未曾听闻。今天做了七年前的Noip,感觉血脉膨胀,不能自已!

1.机器翻译

(translate.pas/c/cpp)

【问题描述】

小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。
这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。
假设内存中有M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过M−1,软件会将新单词存入一个未使用的
内存单元;若内存中已存入M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。
假设一篇英语文章的长度为N 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。

【输入】

输入文件名为translate.in,输入文件共2 行。每行中两个数之间用一个空格隔开。
第一行为两个正整数M 和N,代表内存容量和文章的长度。
第二行为N 个非负整数,按照文章的顺序,每个数(大小不超过1000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。

【输出】

输出文件translate.out 共1 行,包含一个整数,为软件需要查词典的次数。

【输入输出样例1】

translate.in translate.out
3 7
1 2 1 5 4 4 1
5

【输入输出样例 1 说明】

整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:
空:内存初始状态为空。
1. 1:查找单词1 并调入内存。
2. 1 2:查找单词2 并调入内存。
3. 1 2:在内存中找到单词1。
4. 1 2 5:查找单词5 并调入内存。
5. 2 5 4:查找单词4 并调入内存替代单词1。
6. 2 5 4:在内存中找到单词4。
7. 5 4 1:查找单词1 并调入内存替代单词2。
共计查了5 次词典。

【输入输出样例2】

translate.in translate.out
2 10
8 824 11 78 11 78 11 78 8 264
6

【数据范围】

对于10%的数据有M=1,N≤ 5。
对于100%的数据有0

代码

这是我的代码,用的队列模拟。(不过应该还有很多其他方法的。。)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int m,n,a[1005],b[105],tot=0;
int head=1,tail=0,queue[1005];
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;
}

还是不难的哈哈。。。轻松拿到100分。

2.乌龟棋

(tortoise.pas/c/cpp)

【问题描述】

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
乌龟棋的棋盘是一行N 个格子,每个格子上一个分数(非负整数)。棋盘第1 格是唯一
的起点,第N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
……
1 2 3 4 5 …… N
乌龟棋中M 张爬行卡片,分成4 种不同的类型(M 张卡片中不一定包含所有4 种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?

【输入】

输入文件名tortoise.in。输入文件的每行中两个数之间用一个空格隔开。
第1 行2 个正整数N 和M,分别表示棋盘格子数和爬行卡片数。
第2 行N 个非负整数,a1, a2, ……, aN,其中ai 表示棋盘第i 个格子上的分数。
第3 行M 个整数,b1,b2, ……, bM,表示M 张爬行卡片上的数字。
输入数据保证到达终点时刚好用光M 张爬行卡片。

【输出】

输出文件名tortoise.out。
输出只有1 行,1 个整数,表示小明最多能得到的分数。

【输入输出样例1】

tortoise.in tortoise.out
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
7 3

【输入输出样例 1 说明】

小明使用爬行卡片顺序为1,1,3,1,2,得到的分数为6+10+14+8+18+17=73。注意,由于起点是1,所以自动获得第1 格的分数6。

【输入输出样例2】

tortoise.in tortoise.out
13 8
4 96 10 64 55 13 94 53 5 24 89 8 30
1 1 1 1 1 2 4 1
4 5 5

【数据范围】

对于30%的数据有1 ≤ N≤ 30,1 ≤M≤ 12。
对于50%的数据有1 ≤ N≤ 120,1 ≤M≤ 50,且4 种爬行卡片,每种卡片的张数不会超
过20。
对于100%的数据有1 ≤ N≤ 350,1 ≤M≤ 120,且4 种爬行卡片,每种卡片的张数不会超过40;0 ≤ ai ≤ 100,1 ≤ i ≤ N;1 ≤ bi ≤ 4,1 ≤ i ≤M。

代码

刚开始试过深搜,结果,超时。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,maxn=-1,tot=0;
int score[355],card[125],flag[125];
int max(int x,int y)
{
    if(x>=y) return x;
    else return y;
}
void dfs(int pos)
{
    if(pos==n)
    {
        maxn=max(tot,maxn);//不得使用tot,要更改 
        return ;
    }
    for(int i=1;i<=m;i++)
    {
        int now=pos+card[i];//位置 
        if(flag[i]==0&&now<=n)//边界 
        {
            flag[i]=1;
            tot+=score[now];//初始化 
            dfs(now);
            flag[i]=0;
            tot-=score[now];//还原回去 
        }
    }
}
int main()
{
    freopen("tortoise.in","r",stdin);
    freopen("tortoise.out","w",stdout);
    memset(flag,0,sizeof(flag));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&score[i]);
    for(int i=1;i<=m;i++)
    scanf("%d",&card[i]);
    tot+=score[1];//最开头 
    dfs(1);
    printf("%d",maxn);
    return 0;
}

后来想到了dp。。迷了这很明显是dp啊!

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=40+5;
int n,m,dp[N][N][N][N],card[5],a[500];
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",&a[i]);
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%d",&x);
        card[x]++;
    }
    dp[0][0][0][0]=a[1];
    for(int l1=0;l1<=card[1];l1++)
    for(int l2=0;l2<=card[2];l2++)
    for(int l3=0;l3<=card[3];l3++)
    for(int l4=0;l4<=card[4];l4++)
    {
        int s=l1+l2*2+l3*3+l4*4+1;
        if(l1)
        dp[l1][l2][l3][l4]=max(dp[l1][l2][l3][l4],dp[l1-1][l2][l3][l4]+a[s]);
        if(l2)
        dp[l1][l2][l3][l4]=max(dp[l1][l2][l3][l4],dp[l1][l2-1][l3][l4]+a[s]);
        if(l3)
        dp[l1][l2][l3][l4]=max(dp[l1][l2][l3][l4],dp[l1][l2][l3-1][l4]+a[s]);
        if(l4)
        dp[l1][l2][l3][l4]=max(dp[l1][l2][l3][l4],dp[l1][l2][l3][l4-1]+a[s]);
    }
    printf("%d\n",dp[card[1]][card[2]][card[3]][card[4]]);
    return 0;
}

一下子就过了,又是100分。

3.关押罪犯

(prison.pas/c/cpp)

【问题描述】

S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为c 的冲突事件。
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。那么,应如何分配罪犯,才能使Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

【输入】

输入文件名为prison.in。输入文件的每行中两个数之间用一个空格隔开。
第一行为两个正整数N 和M,分别表示罪犯的数目以及存在仇恨的罪犯对数。
接下来的M 行每行为三个正整数aj,bj,cj,表示aj 号和bj 号罪犯之间存在仇恨,其怨气值为 cj。数据保证 a b N j j 1 ≤ < ≤ ,0 < ≤ 1,000,000,000 j c ,且每对罪犯组合只出现一次。

【输出】

输出文件prison.out 共1 行,为Z 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出0。

【输入输出样例】

prison.in prison.out
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
3512

【输入输出样例说明】

罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是3512(由2 号和3 号罪犯引发)。其他任何分法都不会比这个分法更优。

思路

起初想到了并查集的方法,然而。。。并没有成功实现。于是暴力,无奈暴力水平太低。。。只有20分。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,map[500][500],a[50000],b[50000];
int tota=0,totb=0,maxn;
void findmax()
{
    maxn=0;
    for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
    {
        if (map[i][j]>=maxn) 
        {
            maxn=map[i][j];

            break;
        }
    }
}
int judgeA()
{
    for (int i=1;i<=tota;i++)
    for (int j=1;j<=tota;j++)
    if (a[i]==a[j]&&a[i]!=0) return 0;
    else return 1;
}
int judgeB()
{
    for (int i=1;i<=totb;i++)
    for (int j=1;j<=totb;j++)
    if (b[i]==b[j]&&b[i]!=0) return 0;
    else return 1;
}
void woork()
{
    tota++;
    totb++;
    for (int g=1;g<=n;g++)
    for (int h=1;h<=n;h++)
    {
        if (map[g][h]==maxn)
        {
            int aa=0,bb=0;
            for (int d=1;d<=tota;d++)
            {
                if (a[d]!=0) aa++;
                if (b[d]!=0) bb++;
            }
            if (aa+bb>=n) break;
            else
            {
                int maxa=0,maxb=0;
                for (int p=1;p<=tota;p++)
                maxa=max(map[p][g],maxa);
                for (int q=1;q<=totb;q++)
                maxb=max(map[q][h],maxb);
                if (maxa<=maxb/*&&judgeA()==1&&judgeB()==1*/) a[tota]=g,b[totb]=h;
                else a[tota]=h,b[totb]=g;
                map[g][h]=0;
                map[h][g]=0;
            }
        } 
    }
}
int main()
{
    freopen("prison.in","r",stdin);
    freopen("prison.out","w",stdout);
    memset(map,0,sizeof(map));
    scanf("%d%d",&n,&m);
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        map[a][b]=c;
        map[b][a]=c;
    }
    for (int i=1;i<=n;i++)
    {
        findmax();
        woork();
//      judge();
    }
    int maxaa=0,maxbb=0;
    for (int w=1;w<=tota;w++)
    for (int e=1;e<=tota;e++)
    maxaa=max(maxaa,map[a[w]][a[e]]);
    for (int w=1;w<=tota;w++)
    for (int e=1;e<=tota;e++)
    maxbb=max(maxbb,map[b[w]][b[e]]);
    if (maxaa>=maxbb) printf("%d ",maxaa);
    else printf("%d ",maxbb);
    return 0;   
}

后来发现确实是是并查集,代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct edge
{
    int x,y,v;
}cr[100050];
bool cmp(edge a,edge b) 
{
    return a.v>b.v;
}
int n,m;
int an[20050],pr[20050],en[20050];
int father(int x)
{
    if(pr[x]==x) return x;
    pr[x]=father(pr[x]);
    return pr[x];
}
void Union(int x,int y)
{
    int fx=father(x);
    int fy=father(y);
    if(fx!=fy) pr[fx]=fy;
}
int main()
{
    freopen("prison.in","r",stdin);
    freopen("prison.out","w",stdout);
    memset(pr,0,sizeof(pr));
    memset(en,0,sizeof(en));
    scanf("%d%d",&n,&m);
    int x,y,v;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&cr[i].x,&cr[i].y,&cr[i].v);
    }
    sort(cr+1,cr+m+1,cmp);
    for(int i=1;i<=n;i++) {pr[i]=i;en[i]=0;}
    for(int i=1;i<=m;i++)
    {
        x=cr[i].x;
        y=cr[i].y;
        int fx=father(x);
        int fy=father(y);
        if(fx==fy)
        {
            printf("%d\n",cr[i].v);
            return 0;
        }
        if(en[x]==0) en[x]=y;
        else Union(y,en[x]);
        if(en[y]==0) en[y]=x;
        else Union(x,en[y]);
    }
    printf("0\n");
    return 0;
}
一下子就满分了。

4.引水入城**

(flow.pas/c/cpp)

【问题描述】

在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个N 行M 列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。
为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。因此,只有与湖泊毗邻的第1 行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由第N 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利
设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

【输入】

输入文件名为flow.in。输入文件的每行中两个数之间用一个空格隔开。
输入的第一行是两个正整数N 和M,表示矩形的规模。
接下来N 行,每行M 个正整数,依次代表每座城市的海拔高度。

【输出】

输出文件名为flow.out。
输出有两行。如果能满足要求,输出的第一行是整数1,第二行是一个整数,代表最少建造几个蓄水厂;如果不能满足要求,输出的第一行是整数0,第二行是一个整数,代表有几座干旱区中的城市不可能建有水利设施。

【输入输出样例1】

flow.in flow.out
2 5
9 1 5 4 3
8 7 6 1 2
1
1

【样例1 说明】

只需要在海拔为9 的那座城市中建造蓄水厂,即可满足要求。

【输入输出样例2】

flow.in flow.out
3 6
8 4 5 6 4 4
7 3 4 3 3 3
3 2 2 1 1 2
1
3

【样例2 说明】

湖泊
8 4 5 6 4 4
7 3 4 3 3 3
3 2 2 1 1 2
沙漠
上图中,在3 个粗线框出的城市中建造蓄水厂,可以满足要求。以这3 个蓄水厂为源头在干旱区中建造的输水站分别用3 种颜色标出。当然,建造方法可能不唯一。

【数据范围】

本题共有10 个测试数据,每个数据的范围如下表所示:
测试数据编号能否满足要求 N M
1 不能 ≤ 10 ≤ 10
2 不能 ≤ 100 ≤ 100
3 不能 ≤ 500 ≤ 500
4 能 = 1 ≤ 10
5 能 ≤ 10 ≤ 10
6 能 ≤ 100 ≤ 20
7 能 ≤ 100 ≤ 50
8 能 ≤ 100 ≤ 100
9 能 ≤ 200 ≤ 200
10 能 ≤ 500 ≤ 500
对于所有的10 个数据,每座城市的海拔高度都不超过106。

思路

暴力!(不会。。),这个只有10分,所以以后会了更新吧。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=500+5;
int n,m;
int a[N][N]={0};
int main()
{
    freopen("flow.in","r",stdin);
    freopen("flow.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",&a[i][j]);
    if(n==1)
    {
        int tot=0;
        for(int i=1;i<=m;i++)
        if(a[1][i]>=a[1][i-1]&&a[1][i]>=a[1][i+1]) 
        tot++;
        printf("1\n%d",tot);    
    }
    else
    {
        int tot=0;
        for(int j=1;j<=m;j++)
        for(int i=1;i<=m;i++)
        if(a[n][i]>=a[n][i-1]&&a[n][i]>=a[n][i+1]&&a[n][i]>=a[n-1][i])
        {
            tot++;
            a[n][i]=-1;
        }
        printf("0\n%d",tot);
    }
    return 0;
}

这个代码就不要看了。

这就是这次模拟的心得体会,分数一般,重要的是知识与经验的积累。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值