NOIP2010题解

传送门

考查题型 并查集 模拟 dp 搜索

T1

关押罪犯

题目描述

S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为c 的冲突事件。

每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到S 城Z 市长那里。公务繁忙的Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。

在详细考察了N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。

那么,应如何分配罪犯,才能使Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1:
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
输出样例#1:
3512

说明

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

【数据范围】对于30%的数据有N≤ 15。对于70%的数据有N≤ 2000,M≤ 50000。对于100%的数据有N≤ 20000,M≤ 100000。

题解

集合问题 并查集 

先贪心 从大到小排序

因为并查集只能合并,所以我们这样处理。

x和y是敌人,如果把不能放在一个监狱里的人连边是不正确的。

我们假设y+n是y的敌人,那么就是x的朋友 所以x和y+n连边,

同理 y和x+n连边,一旦出现在一个集合里,就输出结束程序

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;

int n,m,fa[40005];
struct P{
    int x,y,val;
    bool operator < (const P &a)const{
        return val>a.val;
    }
}angry[100005];

int f(int x){
    return fa[x]==x?x:fa[x]=f(fa[x]);
}

int main(){
    scanf("%d%d",&n,&m);

    for(int i=1;i<=2*n;i++)fa[i]=i;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&angry[i].x,&angry[i].y,&angry[i].val);
    }
    sort(angry+1,angry+m+1);
    for(int i=1;i<=m;i++){
        int ff=f(angry[i].x),ff_=f(angry[i].y);
        if(ff==ff_){
            printf("%d\n",angry[i].val);
            return 0;
        }
        fa[f(angry[i].x)]=f(angry[i].y+n);fa[f(angry[i].y)]=f(angry[i].x+n);
    }
    printf("0\n");
    return 0;
}

T2

机器翻译

题目背景

小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。

题目描述

这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。

假设内存中有M个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过M-1,软件会将新单词存入一个未使用的内存单元;若内存中已存入M个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。

假设一篇英语文章的长度为N个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。

输入输出格式

输入格式:

 

输入文件共2行。每行中两个数之间用一个空格隔开。

第一行为两个正整数M和N,代表内存容量和文章的长度。

第二行为N个非负整数,按照文章的顺序,每个数(大小不超过1000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。

 

输出格式:

 

包含一个整数,为软件需要查词典的次数。

 

输入输出样例

输入样例#1:
3 7
1 2 1 5 4 4 1
输出样例#1:
5

说明

每个测试点1s

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

对于100%的数据有0<=M<=100,0<=N<=1000。

整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:

空:内存初始状态为空。

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次词典。

题解

模拟

代码

 

#include<iostream>
#include<cstdio>
using namespace std;
int m,n,s=1,t=1,x,ans=1;
int a[1009],ina[1009];
int main(){
    scanf("%d%d",&m,&n);
    scanf("%d",&x);a[1]=x;ina[x]=1; 
    for(int i=2;i<=n;i++){
        scanf("%d",&x);
        if(ina[x])continue;
        ans++;
        if(t-s+1<=m-1)a[++t]=x,ina[x]=1;//wocao没写ina 
        else {
            ina[a[s]]=0;
            s++;a[++t]=x;ina[x]=1;
        }
    }
    printf("%d\n",ans);
    return 0;
}

 

T3

乌龟棋

 

题目背景

 

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。

 

题目描述

 

乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。

 

乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型的卡片,见样例),每种类型的卡片上分别标有1、2、3、4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。

 

游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。

 

很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。

 

现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?

 

输入输出格式

输入格式:

 

 

输入文件的每行中两个数之间用一个空格隔开。

 

第1行2个正整数N和M,分别表示棋盘格子数和爬行卡片数。

 

第2行N个非负整数,a1a2……aN,其中ai表示棋盘第i个格子上的分数。

 

第3行M个整数,b1b2……bM,表示M张爬行卡片上的数字。

 

输入数据保证到达终点时刚好用光M张爬行卡片。

 

 

输出格式:

 

 

输出只有1行,1个整数,表示小明最多能得到的分数。

 

 

 

输入输出样例

 

输入样例#1:
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
输出样例#1:
73

 

说明

 

每个测试点1s

 

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

 

对于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。

题解

首先想当前的状态于什么有关

思考.....

是走到哪里并用了几张牌

所以我们的动态规划坑定有用了几张牌

f[a][b][c][d]表示用了a张1,b张2,c张3,d张4的分数,

而现在走到哪里就是1+a*1+b*2+c*3+d*4

转移的时候就枚举上一次用了哪一张就可以了

上代码

#include<iostream>
#include<cstdio>
using namespace std;
int n,m,x;
int v[389],cnt[5],f[42][42][42][42];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&v[i]);
    for(int i=1;i<=m;i++)scanf("%d",&x),cnt[x]++;
    for(int a=0;a<=cnt[1];a++){
        for(int b=0;b<=cnt[2];b++){
            for(int c=0;c<=cnt[3];c++){
                for(int d=0;d<=cnt[4];d++){
                    f[a][b][c][d]=max(max(f[max(a-1,0)][b][c][d],f[a][max(0,b-1)][c][d]),max(f[a][b][max(0,c-1)][d],f[a][b][c][max(0,d-1)]));
                    f[a][b][c][d]+=v[1+a*1+b*2+c*3+d*4];
                }
            }
        }
    }
    printf("%d\n",f[cnt[1]][cnt[2]][cnt[3]][cnt[4]]); 
    return 0;
}

T4

引水入城

题目描述

在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个N 行M 列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。

为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。

因此,只有与湖泊毗邻的第1 行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第N 行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1:
【输入样例1】
2 5
9 1 5 4 3
8 7 6 1 2

【输入样例2】
3 6
8 4 5 6 4 4
7 3 4 3 3 3
3 2 2 1 1 2
输出样例#1:
【输出样例1】
1
1

【输出样例2】
1
3

说明

【样例1 说明】

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

【样例2 说明】

上图中,在3 个粗线框出的城市中建造蓄水厂,可以满足要求。以这3 个蓄水厂为源头

在干旱区中建造的输水站分别用3 种颜色标出。当然,建造方法可能不唯一。

【数据范围】

 

不发题解对不起我做这个题.....

题目大意:从第一行中最少选出几个点深搜 能把最后一行的点都搜到

讲一下我的思考过程

如果最后一行每个点都能流到水,那么一定有一条道路从这个点开始递增向上

可是如果从最后一行开始向上找,找到的并不唯一。所以我们从第一行向下搜,

假设每个点都是蓄水池,看看能不能流满最后一行。这里我用的bfs

然后我们就可以分别找到从第一行的每个点建蓄水站能流到的最后一行的区间。

也就是说假设我们在第一行第4个位置建蓄水池,能流到最后一行的[3,4]。

可是万一这段区间不连续怎么办??自己画画就发现,只要区间不连续,最后

一行一定流不满。

然后我们知道第一行每一个点能覆盖最后一行的区间。我们最终的目的是选择

最少的点流满最后一行,也就是说选最少的区间覆盖最后一行。

这就成了一个区间覆盖问题。可贪心 不过我贪心翻车了。

讲一下dp f[i]表示覆盖1--i所需最少的区间。最后答案就是f[m]

f[i]=min(f[i],f[xd[k].l-1]+1)状态转移方程。坑点:这个图可能只有一行,坑我好久嘤嘤嘤

上代码 这个代码在vijos上是A的,luogu tle第5个点 但始终要看思路啊

#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x7fffffff
using namespace std;
int n,m,cnt,maxr,minl,head,tail;
int k[502],vis[502][502],h[502][502],f[502];
struct E{
    int x,y;
}q[1000005];
struct X{
    int l,r;
}xd[520];
int mx[4]={0,1,0,-1},
    my[4]={-1,0,1,0};
void bfs(int x,int y){
    head=tail=1;vis[x][y]=1;
    q[1].x=x;q[1].y=y;
    if(x==n){
    minl=min(minl,y);maxr=max(maxr,y);
    if(k[y]==0)k[y]=1,cnt++;
    }//忘了当前的就是最后一行啊 啊啊啊 
    while(head<=tail){
        int nowx=q[head].x,nowy=q[head++].y;
        for(int i=0;i<=3;i++){
            int xx=nowx+mx[i],yy=nowy+my[i];
            if(h[xx][yy]<h[nowx][nowy]&&vis[xx][yy]==0&&xx>=1&&xx<=n&&yy>=1&&yy<=m){
                vis[xx][yy]=1;
                if(xx==n){
                    minl=min(minl,yy);maxr=max(maxr,yy);
                    if(k[yy]==0){k[yy]=1;cnt++;}
                }
                q[++tail].x=xx;q[tail].y=yy;
            }
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    register int i,j;
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            scanf("%d",&h[i][j]);
        }
    }
    for(i=1;i<=m;i++){
        memset(vis,0,sizeof(vis));
        minl=inf;maxr=-inf;
        bfs(1,i);
        xd[i].l=minl;xd[i].r=maxr;
    }
//    cout<<cnt<<endl;
    if(cnt!=m){
        printf("0\n%d",m-cnt);
        return 0;
    }
    for(i=1;i<=m;i++){
        f[i]=inf;
        for(int j=1;j<=m;j++){
            if(xd[j].l<=i&&i<=xd[j].r)
            f[i]=min(f[i],f[xd[j].l-1]+1);
        }
    }
    printf("1\n%d",f[m]);
    return 0;
}

 

posted @ 2017-08-24 11:23  ANhour  阅读(302)  评论(0编辑  收藏  举报