凡人升天传7——NOIP2010 提高组复赛题解
本蒟蒻在考试时最后一道直接报零*__*,悲痛欲绝,因此在这里著下本题解。
可恶的西西弗
虽然题目做的很垃圾,但在写题解中途不得不感叹除了最后一道,其他真是好水题呀!!!
———————————————————————————————————————————
目录
一、机器翻译
二、乌龟棋
三、关押罪犯
四、引水入城
总结
一、机器翻译
题目背景
小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章。
题目描述
这个翻译软件的原理很简单,它只是从头到尾,依次将每个英文单词用对应的中文含义来替换。对于每个英文单词,软件会先在内存中查找这个单词的中文含义,如果内存中有,软件就会用它进行翻译;如果内存中没有,软件就会在外存中的词典内查找,查出单词的中文含义然后翻译,并将这个单词和译义放入内存,以备后续的查找和翻译。
假设内存中有 M 个单元,每单元能存放一个单词和译义。每当软件将一个新单词存入内存前,如果当前内存中已存入的单词数不超过 M−1,软件会将新单词存入一个未使用的内存单元;若内存中已存入 M 个单词,软件会清空最早进入内存的那个单词,腾出单元来,存放新单词。
假设一篇英语文章的长度为 N 个单词。给定这篇待译文章,翻译软件需要去外存查找多少次词典?假设在翻译开始前,内存中没有任何单词。
输入格式
共 22 行。每行中两个数之间用一个空格隔开。
第一行为两个正整数 M,N,代表内存容量和文章的长度。
第二行为 N 个非负整数,按照文章的顺序,每个数(大小不超过 10001000)代表一个英文单词。文章中两个单词是同一个单词,当且仅当它们对应的非负整数相同。
输出格式
一个整数,为软件需要查词典的次数。
输入输出样例
输入
3 7
1 2 1 5 4 4 1
输出
5
说明/提示
样例解释
整个查字典过程如下:每行表示一个单词的翻译,冒号前为本次翻译后的内存状况:
1
:查找单词 1 并调入内存。1 2
:查找单词 2 并调入内存。1 2
:在内存中找到单词 1。1 2 5
:查找单词 5 并调入内存。2 5 4
:查找单词 4 并调入内存替代单词 1。2 5 4
:在内存中找到单词 4。5 4 1
:查找单词 1 并调入内存替代单词 2。
共计查了 55 次词典。
本题主要考查队列的基本应用,基本通过STL容器中的队列的插入与弹出可以实现
为了方便查询字典中是否含有该单词,如果直接遍历,时间复杂度会是 ,因此,在代码中采用哈希表,可以将时间复杂度简化到,又因为本题范围较小,因此不会存在哈希冲突
基本代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll read(){//快读 ll ans = 0 , f = 1;char ch = getchar();while(!isdigit(ch)){f *= (ch == '-') ? -1 : 1;ch = getchar();}while(isdigit(ch)){ans = (ans << 3) + (ans << 1) + (ch ^ 48);ch = getchar();}return ans * f;
}
const int MAXN = 1e4 + 5;
queue<int> q;
int g[MAXN],ans;//g数组使用简单的哈希,找出字典中是否有该单词
signed main(){int m = read() , n = read();for(int i = 1;i <= n; i ++){int a = read();if(g[a] == 0){//判断字典中是否含有该单词 q.push(a); //将单词放入字典 g[a] ++;ans++;//每插入一个单词,就查找了一次 } if(q.size() > m){//字典容量已满 g[q.front()] = 0;//哈希表中清除队首单词 q.pop();//弹出队首单词 }}printf("%d", ans);
}
//3 7
//1 2 1 5 4 4 1
另外,也可使用手工建队列
#include<bits/stdc++.h>
using namespace std;
int n,m,x,ans,l,r,a[1005],b[1005];
int main(){cin>>m>>n;l=0;r=0;for(int i=1;i<=n;i++){scanf("%d",&x);//边读入边做if(a[x]==0){ans++;r++;b[r]=x;a[x]=1;//因为每次遇到新单词都要做这些操作,不如搬到判断语句外做,这样程序更简洁if(r>m){l++;a[b[l]]=0;}}}cout<<ans;return 0;//千万不能忘记打这句,不然在比赛中会出错
}
时间复杂度均为
二、乌龟棋
题目背景
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。
题目描述
乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有44种类型的卡片,见样例),每种类型的卡片上分别标有1,2,3,41,2,3,4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。
很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。
现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入格式
每行中两个数之间用一个空格隔开。
第11行22个正整数N,M,分别表示棋盘格子数和爬行卡片数。
第22行N个非负整数,1,2,…,a1,a2,…,aN,其中ai表示棋盘第i个格子上的分数。
第33行M个整数1,2,…,b1,b2,…,bM,表示M张爬行卡片上的数字。
输入数据保证到达终点时刚好用光M张爬行卡片。
输出格式
11个整数,表示小明最多能得到的分数。
输入输出样例
输入
9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1
输出
73
本题明显的背包dp题,将四种爬行卡牌看作四种物品,对应了其价值(当前步数所代表的分数值),推出状态转移方程,从而求解
我们设下以下变量:
作为爬行卡牌走一步的使用a张,走两步的使用b张,走三步的使用c张,走四步的使用d张所得到的最大分数
表示第i张所拥有的分数
表示目前所走步数,那么代表了当前位置的分数
本题的背包问题的原理十分简单——“少其中一张牌,增加这张牌所代表的步数,dp数组加上走后位置的分数,四种情况求最大值”
因此,状态转移方程就十分明了了:
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll read(){ll ans = 0 , f = 1;char ch = getchar();while(!isdigit(ch)){f *= (ch == '-') ? -1 : 1;ch = getchar();}while(isdigit(ch)){ans = (ans << 3) + (ans << 1) + (ch ^ 48);ch = getchar();}return ans * f;
}
//int maxn(int a,int b,int c,int d,int e){
// if(a > b && a > c && a > b && a > e) return a;
// if(b > c && b > a && b > d && b > e) return b;
// if(c > a && c > b && c > d && c > e) return c;
// if(d > a && d > b && d > c && d > e) return d;
// else return e;
//}
const int MAXN = 355;
const int MAXX = 100;
int n,m,t[MAXN],bx,w[5],dp[MAXX][MAXX][MAXX][MAXX];
int main(){n = read() , m = read();for(int i = 1;i <= n; i ++){t[i] = read();//t[i]为每一步所代表的分数 }for(int j = 1;j <= m; j ++){w[read()] ++;//w[i]为走i步卡牌的数量 }dp[0][0][0][0] = t[1]; for(int a = 0;a <= w[1]; a ++){for(int b = 0;b <= w[2]; b ++){ for(int c = 0;c <= w[3]; c ++){for(int d = 0;d <= w[4]; d ++){ int nxt = 1 + a * 1 + b * 2 + c * 3 + d * 4;//nxt为当前走到了第几步
// dp[a][b][c][d] = maxn(dp[a][b][c][d] , dp[a - 1][b][c][d] + t[nxt] , dp[a][b - 1][c][d] + t[nxt] , dp[a][b][c - 1][d] + t[nxt] , dp[a][b][c][d - 1] + t[nxt]);if(a != 0) dp[a][b][c][d] = max(dp[a][b][c][d] , dp[a - 1][b][c][d] + t[nxt]);//走第一种牌 if(b != 0) dp[a][b][c][d] = max(dp[a][b][c][d] , dp[a][b - 1][c][d] + t[nxt]);//走第二种牌 if(c != 0) dp[a][b][c][d] = max(dp[a][b][c][d] , dp[a][b][c - 1][d] + t[nxt]);//走第三种牌 if(d != 0) dp[a][b][c][d] = max(dp[a][b][c][d] , dp[a][b][c][d - 1] + t[nxt]);//走第四种牌 }}}}printf("%d",dp[w[1]][w[2]][w[3]][w[4]]);return 0;
}
//9 5
//6 10 14 2 8 8 18 5 17
//1 3 1 2 1 //13 8
//4 96 10 64 55 13 94 53 5 24 89 8 30
//1 1 1 1 1 2 4 1
时间复杂度:
注意,在这里必须判断a、b、c、d的是否非零,一旦为零,就不能使用这种牌,一旦使用,数组就会越界,导致答案错误。因此在代码中使用状态转移方程时需添加判断是否为零。
谨记!!!
三、关押罪犯
先看题目
题目描述
S 城现有两座监狱,一共关押着 名罪犯,编号分别为 。他们之间的关系自然也极不和谐。很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。如果两名怨气值为 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 的冲突事件。
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了 名罪犯间的矛盾关系后,警察局长觉得压力巨大。他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。
那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?
输入格式
每行中两个数之间用一个空格隔开。第一行为两个正整数 ,,分别表示罪犯的数目以及存在仇恨的罪犯对数。接下来的行每行为三个正整数 ,,,表示号和号罪犯之间存在仇恨,其怨气值为。每对罪犯组合只出现一次。
输出格式
共 11 行,为 Z 市长看到的那个冲突事件的影响力。如果本年内监狱中未发生任何冲突事件,请输出 0
。
输入输出样例
输入
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
输出
3512
说明/提示
【输入输出样例说明】罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件影响力是 35123512(由 22 号和 33 号罪犯引发)。其他任何分法都不会比这个分法更优。
定眼一看,哟哟哟!并查集
既然所有罪犯都带有怒气值(权值),那么排序是非常必要的!对吧!
另外,再结合敌人的敌人和自己一起在一个监狱的规则,这样就完成了合并。
而查找时,我们会发现有两个罪犯会像磁铁一样吸在一起,永不分开(上辈子的缘分),这两个就是我们要找的那两个男人,是他是他就是他,将它输出,然后AC
当然,切记:如果我们找不到冲突的话,得输入0,不然后悔莫及
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll read(){ll ans = 0 , f = 1;char ch = getchar();while(!isdigit(ch)){f *= (ch == '-') ? -1 : 1;ch = getchar();}while(isdigit(ch)){ans = (ans << 3) + (ans << 1) + (ch ^ 48);ch = getchar();}return ans * f;
}
const int MAXN = 1e5 + 5;
const int MAXX = 2e5 + 5;
struct data{int x;int y;int z;
};//结构体便于排序的变换
struct data f[MAXN];
int n,m,a[MAXX],b[MAXX],i;
bool cmp(data a,data b){//将结构体排序规则写入sort return a.z > b.z;
}
int find(int x){if(a[x] == x) return x;a[x] = find(a[x]);return a[x];
}
void ad(int x,int y){//合并x = find(a[x]);y = find(a[y]);a[x] = y;
}
bool check(int x,int y){//查找x = find(x);y = find(y);if(x == y) return true;return false;
}
signed main(){n = read() , m = read();for(i = 1;i <= n; i ++) a[i] = i;for(i = 1;i <= m; i ++)scanf("%d%d%d", &f[i].x , &f[i].y , &f[i].z);sort(f + 1 , f + m + 1 , cmp);for(i = 1;i <= m + 1; i ++){if(check(f[i].x , f[i].y)){printf("%d", f[i].z);break;}//当两个罪犯已经在同一监狱,输出答案 else{if(!b[f[i].x]) b[f[i].x] = f[i].y;//将敌人标记 else{ad(b[f[i].x] , f[i].y);}//将敌人的敌人合并if(!b[f[i].y])b[f[i].y] = f[i].x;else{ad(b[f[i].y] , f[i].x);}}}return 0;
}
四、引水入城
题目描述
在一个遥远的国度,一侧是风景秀美的湖泊,另一侧则是漫无边际的沙漠。该国的行政区划十分特殊,刚好构成一个 行× 列的矩形,如上图所示,其中每个格子都代表一座城市,每座城市都有一个海拔高度。
为了使居民们都尽可能饮用到清澈的湖水,现在要在某些城市建造水利设施。水利设施有两种,分别为蓄水厂和输水站。蓄水厂的功能是利用水泵将湖泊中的水抽取到所在城市的蓄水池中。
因此,只有与湖泊毗邻的第11 行的城市可以建造蓄水厂。而输水站的功能则是通过输水管线利用高度落差,将湖水从高处向低处输送。故一座城市能建造输水站的前提,是存在比它海拔更高且拥有公共边的相邻城市,已经建有水利设施。由于第行的城市靠近沙漠,是该国的干旱区,所以要求其中的每座城市都建有水利设施。那么,这个要求能否满足呢?如果能,请计算最少建造几个蓄水厂;如果不能,求干旱区中不可能建有水利设施的城市数目。
输入格式
每行两个数,之间用一个空格隔开。输入的第一行是两个正整数,表示矩形的规模。接下来行,每行个正整数,依次代表每座城市的海拔高度。
输出格式
两行。如果能满足要求,输出的第一行是整数,第二行是一个整数,代表最少建造几个蓄水厂;如果不能满足要求,输出的第一行是整数,第二行是一个整数,代表有几座干旱区中的城市不可能建有水利设施。
输入输出样例
输入 #1
2 5
9 1 5 4 3
8 7 6 1 2
输出 #1
1
1
输入 #2
3 6
8 4 5 6 4 4
7 3 4 3 3 3
3 2 2 1 1 2
输出 #2
1
3
说明/提示
【样例1 说明】
只需要在海拔为99 的那座城市中建造蓄水厂,即可满足要求。
【样例2 说明】
上图中,在33个粗线框出的城市中建造蓄水厂,可以满足要求。以这33个蓄水厂为源头在干旱区中建造的输水站分别用3 种颜色标出。当然,建造方法可能不唯一。
【数据范围】
当时本帅哥练题时一眼就看出来这道题时搜索以及动态规划,可惜实力太强弱没做出来
由于这道题我也没做起,就结合了一下洛谷大佬的题解 题解 P1514 引水入城 - 小朋友的博客 - 洛谷博客
其实这道题关键就在于寻找状态转移方程
我们可以用动态规划来求出能够满足时最少需要的蓄水厂的个数,在宽搜的时候求出每个点所能够去到的最左的点(左端点)和最右的点(右端点),然后用来表示和之间最少需要建多少座蓄水厂,并且用来表示最少需要多少座蓄水厂(注意一开始要将这两个数组初始化为)。我们可以将可以到达其他出口且不被其他入口所到达的点的左端点至右端点的值改为
由此得解,最后的答案即为。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll read(){ll ans = 0 , f = 1;char ch = getchar();while(!isdigit(ch)){f *= (ch == '-') ? -1 : 1;ch = getchar();}while(isdigit(ch)){ans = (ans << 3) + (ans << 1) + (ch ^ 48);ch = getchar();}return ans * f;
}
const int MAXN = 1e3 + 5;
int n, m;
int dx[] = {-1, 0, 1, 0} , dy[] = {0, 1, 0, -1};
int vis[MAXN][MAXN];
int h[MAXN][MAXN], l[MAXN][MAXN], r[MAXN][MAXN];
void dfs(int x, int y){vis[x][y] = 1;for (int i = 0; i <= 3; i ++) {int x1 = x + dx[i] , y1 = y + dy[i];if(x1 < 1 || x1 > n || y1 < 1 || y1 > m || h[x][y] <= h[x1][y1]) continue;if(!vis[x1][y1]) dfs(x1, y1);l[x][y] = min(l[x][y], l[x1][y1]);r[x][y] = max(r[x][y], r[x1][y1]);}
}
signed main() {n = read() , m = read();memset(l, 0x3f3f3f3f, sizeof(l));for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {h[i][j] = read();if(i == n) l[i][j] = r[i][j] = j;}} for(int i = 1; i <= m; i ++){if (!vis[1][i]) dfs(1 , i);}bool check = true;int ans = 0;for(int i = 1; i <= m; i ++){if(!vis[n][i]){check = false;ans++; }}if(!check){printf("0\n%d\n", ans);return 0;}int left = 1, right = r[1][1];//统计区间数 while(left <= m) {for(int i = 1; i <= m; i ++){if(l[1][i] <= left){right = max(right, r[1][i]);}}left = right + 1;ans++;}printf("1\n%d\n", ans);return 0;
}
总结
以上就是NOIP2010 提高组复赛题解,做完一套真题其实收获还是蛮大的,看我下周拿下11年复赛题。西西弗,可恶!!!
凡人升天传7——NOIP2010 提高组复赛题解相关推荐
- NOIP2010 提高组 复赛 translate 机器翻译
NOIP2010 提高组 复赛 translate 机器翻译 1.读题,很快弄明题意,单词不在内存中就查字典,统计查字典次数. 2.内存采用队列方式.统计进队列次数,即为查询次数. 3.程序很快编好, ...
- NOIP 2008 提高组 复赛 message 传字条
NOIP 2008 提高组 复赛 message 传字条 1.样例很快模拟成功,但感觉是凑出来的,没有章法. 2.深度优先遍历,但感觉容易超时. 3.动态规划?翻看他人代码,发现动态规划的写法,确实想 ...
- #洛谷oj:P1525 [NOIP2010 提高组] 关押罪犯
洛谷oj:P1525 [NOIP2010 提高组] 关押罪犯 #题目描述 #一看很明显是贪心算法 加排序 因为 这个中间最大值的那一对肯定是不会在一起的 从大到小来看 所有点对都尽量不要在一个监狱 # ...
- NOIP2013 提高组复赛解题报告
NOIP2013 提高组复赛 day1 day\;1 1002. 火柴排队 贪心+数据结构/归并排序 这个"相邻交换"让我联想到了NOIP2012_day1_task2_game那 ...
- NOIP2010提高组题解
[NOIP2010 提高组] 机器翻译 题目:[NOIP2010 提高组] 机器翻译 题目背景 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章. 题目描述 这个翻译软件的原理很简单 ...
- NOIP 提高组 复赛 历年 试题
NOIP 提高组 复赛 历年 试题 NOIP 2017 提高组 复赛 试题 https://wenku.baidu.com/view/70de9e29854769eae009581b6bd97f ...
- #185. [NOIP2016 提高组] 蚯蚓题解
#185. [NOIP2016 提高组] 蚯蚓题解 题目描述 本题中,我们将用符号 ⌊c⌋\lfloor c \rfloor⌊c⌋ 表示对 ccc 向下取整,例如:⌊3.0⌋=⌊3.1⌋=⌊3.9⌋= ...
- 洛谷-神奇的幻方-NOIP2015提高组复赛
题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,--,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. ...
- NOIP 2018提高组复赛C/C++试题及答案详解
NOIP 2018提高组历年真题 CCF NOIP2018 初赛提高组 C++语言试题 第 1 页,共9 页 第二十四届全国青少年信息学奥林匹克联赛初赛 提高组 C++语言试题 竞赛时间:2018 年 ...
最新文章
- linux 逆向工具 radare2入门
- Linux下编译vtk的java版本,vtk在linux下的安裝(12月8日更新)
- Android中利用隐式意图发送短信
- 简述基于EDA技术的FPGA设计
- homeassistant树莓派cpu_集成ESP8266的WiFi RGB灯泡接入Home Assistant
- 构建iOS持续集成平台(三)——CI服务器与自动化部署
- web.xml上监听器作用
- python 读取地震道头数据_python地震数据可视化详解
- rudesocket如何使用_c++ socket 客户端库 socks5 客户端 RudeSocket™ Open Source C++ Socket Library...
- Excel2007数据透视表学习(一)
- Javascript 汉字拼音排序
- React Native 0.20官方入门教程
- OpenGL基础8:SOIL库
- Android_撕衣服小案例
- 常见电脑主机报警提示音及对应原因
- SQL数据分析常用案例总结
- 数学分析教程(科大)——2.5笔记+习题
- 百度给创新员工发2000w奖金........
- 从头开始搞懂 MySQL(07)为什么同一条 SQL 时快时慢
- 大星星学物联网概览篇-硬件
热门文章
- Docker搭建3主3从Redis Cluster集群
- PS日期,天数的处理函数
- 剑网三三测服务器维护,郭炜炜深夜两点回应谣言,剑网3凌晨紧急维护:优化精力系统...
- 浪潮之巅第二章 — 蓝色巨人(IBM)
- 数据库评书---个人理解+基础知识(DBMS三级模式和二级映象)
- 如何在线生成Word文档?一种极简,极强大的方法,支持图片表格等各种格式
- linux编辑器 line,Linux 编辑器(vi、emacs、grep、gawk、sed等) zz
- mybatis 一对多 两种查询方式
- Ubuntu---sudo命令介绍
- Java常用类库的介绍和使用(例如String、Math、Scanner等)