一文解答你的所有疑惑【全国信息学奥林匹克联赛(NOIP1010)复赛】

209 阅读2分钟

学习目标:持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第17天,点击查看活动详情

@TOC 本文所有代码均可放心食用

题目1

1.数字统计 (two.pas/c/cpp) 【问题描述】 请统计某个给定范围[L, R]的所有整数中,数字 2 出现的次数。 比如给定范围[2, 22],数字 2 在数 2 中出现了 1 次,在数 12 中出现 1 次,在数 20 中出现 1 次,在数 21 中出现 1 次,在数 22 中出现 2 次,所以数字 2 在该范围内一共出现了 6 次。 【输入】 输入文件名为 two.in。 输入共 1 行,为两个正整数 L 和 R,之间用一个空格隔开。 【输出】 输出文件名为 two.out。 输出共 1 行,表示数字 2 出现的次数。 【输入输出样例 1】 two.in two.out 2 22 6 【输入输出样例 2】 two.in two.out 2 100 20 【数据范围】 1 ≤ L ≤R≤ 10000。

思路

这道题只需要一个循环然后每一位%10后/10处理即可 例如:123%10=3!=2,123/10=12,12%10=2,12/10=1,1%10=1,1/10=0; 所以无论数字是几位数都可以这样处理

代码

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char s;
int n,m;
int main(){
	freopen("two.pas.in","r",stdin);
	freopen("two.pas.out","w",stdout);
	cin>>n>>m;
	int ans=0;
	for(int i=n;i<=m;i++){
		int w=i;
		while(w){
			
			int c=w%10;
			if(c==2){
			ans+=1;
			}
			w/=10;
			
	}
	}
	cout<<ans;
}

题目2

2.接水问题 (water.pas/c/cpp) 【问题描述】 学校里有一个水房,水房里一共装有 m 个龙头可供同学们打开水,每个龙头每秒钟的供水量 相等,均为 1。 现在有 n 名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从 1 到 n 编号,i 号同学的接水量为 wi。接水开始时,1 到 m 号同学各占一个水龙头,并同时打开水龙头 接水。当其中某名同学 j 完成其接水量要求 wj 后,下一名排队等候接水的同学 k 马上接替 j 同 学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即 j 同学第 x 秒结束 时完成接水, 则 k 同学第 x+1 秒立刻开始接水。 若当前接水人数 n’不足 m,则只有 n’个 龙头供水,其它 m−n’个龙头关闭。 现在给出 n 名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。 全国信息学奥林匹克联赛(NOIP1010)复赛 普及组 第 3 页 共 7 页 【输入】 输入文件名为 water.in。 第 1 行 2 个整数 n 和 m,用一个空格隔开,分别表示接水人数和龙头个数。 第 2 行 n 个整数 w1、w2、„„、wn,每两个整数之间用一个空格隔开,wi 表示 i 号同学 的接水量。 【输出】 输出文件名为 water.out。 输出只有一行,1 个整数,表示接水所需的总时间。 【输入输出样例 1】 water.in water.out 5 3 4 4 1 2 1 4 【输入输出样例 1 解释】 第 1 秒,3 人接水。第 1 秒结束时,1、2、3 号同学每人的已接水量为 1,3 号同学接完水, 4 号同学接替 3 号同学开始接水。 第 2 秒,3 人接水。第 2 秒结束时,1、2 号同学每人的已接水量为 2,4 号同学的已接水 量为 1。 第 3 秒,3 人接水。第 3 秒结束时,1、2 号同学每人的已接水量为 3,4 号同学的已接水 量为 2。4 号同学接完水,5 号同学接替 4 号同学开始接水。 第 4 秒,3 人接水。第 4 秒结束时,1、2 号同学每人的已接水量为 4,5 号同学的已接水 量为 1。1、2、5 号同学接完水,即所有人完成接水。 总接水时间为 4 秒。 【输入输出样例 2】 water.in water.out 8 4 23 71 87 32 70 93 80 76 163 【数据范围】 1 ≤ n ≤ 10000,1 ≤m≤ 100 且 m≤ n; 1 ≤wi ≤ 100。

思路

这道题我们可以循环每一秒,也就是当循环走过一遍,答案就多了一米秒,在接水的过程中如果有一个满了就可以继续接1+m个桶,以此类推就可以完成题目

代码

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int m[1000006];
int n,t;
int main(){
	freopen(" water.in","r",stdin);
	freopen(" water.out","w",stdout);
cin>>n>>t;
for(int i=1;i<=n;i++){
	cin>>m[i];
}
int c=t+1;
int ans=0;
while(c<=n+t){//因为c等于t+1,所以当处理到n个人的时候c的值为n+t
	for(int i=1;i<=t;i++){
		m[i]--;
		if(!m[i]){
			m[i]=m[c];
			c+=1;
			
		}
	}
	ans++;
}
cout<<ans;
}

题目3

3.导弹拦截 (missile.pas/c/cpp) 【问题描述】 经过 11 年的韬光养晦,某国研发出了一种新的导弹拦截系统,凡是与它的距离不超过其工 作半径的导弹都能够被它成功拦截。当工作半径为 0 时,则能够拦截与它位置恰好相同的导弹。 但该导弹拦截系统也存在这样的缺陷:每套系统每天只能设定一次工作半径。而当天的使用代价, 就是所有系统工作半径的平方和。 某天,雷达捕捉到敌国的导弹来袭。由于该系统尚处于试验阶段,所以只有两套系统投入工作。 如果现在的要求是拦截所有的导弹,请计算这一天的最小使用代价。 全国信息学奥林匹克联赛(NOIP1010)复赛 普及组 第 4 页 共 7 页 【输入】 输入文件名 missile.in。 第一行包含 4 个整数 x1、y1、x2、y2,每两个整数之间用一个空格隔开,表示这两套导弹拦 截系统的坐标分别为(x1, y1)、(x2, y2)。 第二行包含 1 个整数 N,表示有 N 颗导弹。接下来 N 行,每行两个整数 x、y,中间用 一个空格隔开,表示一颗导弹的坐标(x, y)。不同导弹的坐标可能相同。 【输出】 输出文件名 missile.out。 输出只有一行,包含一个整数,即当天的最小使用代价。 【提示】 两个点(x1, y1)、(x2, y2)之间距离的平方是(x1− x2)2 +(y1− y2)2。 两套系统工作半径 r1、r2 的平方和,是指 r1、r2 分别取平方后再求和,即 r12 +r22。 【输入输出样例 1】 missile.in missile.out 0 0 10 0 2 -3 3 10 0 18 【样例 1 说明】 样例 1 中要拦截所有导弹,在满足最小使用代价的前提下,两套系统工作半径的平方分别为 18 和 0。 【输入输出样例 2】 missile.in missile.out 0 0 6 0 5 -4 -2 -2 3 4 0 6 -2 9 1 30 【样例 2 说明】 样例中的导弹拦截系统和导弹所在的位置如下图所示。要拦截所有导弹,在满足最小使用代价 的前提下,两套系统工作半径的平方分别为 20 和 10。 全国信息学奥林匹克联赛(NOIP1010)复赛 普及组 第 5 页 共 7 页 【数据范围】 对于 10%的数据,N = 1 对于 20%的数据,1 ≤ N ≤ 2 对于 40%的数据,1 ≤ N ≤ 100 对于 70%的数据,1 ≤ N ≤ 1000 对于 100%的数据,1 ≤ N ≤ 100000,且所有坐标分量的绝对值都不超过 1000。

思路

题目中的要求就是用最小的半径和拦截所有导弹,所以我们可以先将导弹按照到1号系统的距离从小到大排序,然后进行处理。这道题的难点在于数据范围过大,n为1*10^5,如果二维遍历就会炸,所以需要线性排序,这里要用到sort、记得加入algorithm头文件

代码

#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
struct edge{
	int x,y;
	bool operator <(const edge &a)const{
		return x<a.x;//这里的意思是在排序结构体的过程中以x,也就是到1的距离作为排序对象,最终结果就是按照导弹到1的距离从小到大排序
	}
}e[100005];
int x1,x2,y2,n;
int yl;
int main(){
freopen(" missile.in","r",stdin);
freopen(" missile.out","w",stdout);
	cin>>x1>>yl>>x2>>y2>>n;
	for(int i=1;i<=n;i++){
		int a,b;
		cin>>a>>b;
		e[i].x=(x1-a)*(x1-a)+(yl-b)*(yl-b);
		e[i].y=(x2-a)*(x2-a)+(y2-b)*(y2-b);//这里记录距离
	}
	sort(e+1,e+n+1);
	int ans=e[n].x;//这里是为了处理当导弹只有1枚的结果
	int c=0;
	
	for(int i=n-1;i>=1;i--){
		 c=max(c,e[i+1].y);//如果加入的导弹到2的距离更大,那么更新数据,这里取最大值是因为当拦截了最远的导弹,那么其他的导弹就一起拦截了的
		ans=min(ans,c+e[i].x);
	
	}
	cout<<ans;
	
}

题目4

4.三国游戏 (sanguo.pas/c/cpp) 【问题描述】 小涵很喜欢电脑游戏,这些天他正在玩一个叫做《三国》的游戏。 在游戏中, 小涵和计算机各执一方, 组建各自的军队进行对战。 游戏中共有 N 位武将 (N 为偶数且不小于 4) ,任意两个武将之间有一个“默契值” ,表示若此两位武将作为一对组合作 战时,该组合的威力有多大。游戏开始前,所有武将都是自由的(称为自由武将,一旦某个自由武 将被选中作为某方军队的一员,那么他就不再是自由武将了) ,换句话说,所谓的自由武将不属 于任何一方。游戏开始,小涵和计算机要从自由武将中挑选武将组成自己的军队,规则如下:小涵 先从自由武将中选出一个加入自己的军队,然后计算机也从自由武将中选出一个加入计算机方的军 队。接下来一直按照“小涵→计算机→小涵→„„”的顺序选择武将,直到所有的武将被双方均分 完。然后,程序自动从双方军队中各挑出一对默契值最高的武将组合代表自己的军队进行二对二比 武,拥有更高默契值的一对武将组合获胜,表示两军交战,拥有获胜武将组合的一方获胜。 已知计算机一方选择武将的原则是尽量破坏对手下一步将形成的最强组合,它采取的具体策略 如下:任何时刻,轮到计算机挑选时,它会尝试将对手军队中的每个武将与当前每个自由武将进行 一一配对,找出所有配对中默契值最高的那对武将组合,并将该组合中的自由武将选入自己的军队。 下面举例说明计算机的选将策略,例如,游戏中一共有 6 个武将,他们相互之间的默契值如 全国信息学奥林匹克联赛(NOIP1010)复赛 普及组 第 6 页 共 7 页 下表所示 双方选将过程如下所示:

小涵想知道,如果计算机在一局游戏中始终坚持上面这个策略,那么自己有没有可能必胜?如 果有,在所有可能的胜利结局中,自己那对用于比武的武将组合的默契值最大是多少? 假设整个游戏过程中,对战双方任何时候均能看到自由武将队中的武将和对方军队的武将。为 了简化问题,保证对于不同的武将组合,其默契值均不相同。 【输入】 输入文件名为 sanguo.in,共 N 行。 第一行为一个偶数 N,表示武将的个数。 第 2 行到第 N 行里,第(i+1)行有(N−i)个非负整数,每两个数之间用一个空格隔开, 表示 i 号武将和 i+1,i+2,„„,N 号武将之间的默契值(0 ≤默契值≤ 1,000,000,000)。 【输出】 输出文件 sanguo.out 共 1 或 2 行。 若对于给定的游戏输入,存在可以让小涵获胜的选将顺序,则输出 1,并另起一行输出所有获 全国信息学奥林匹克联赛(NOIP1010)复赛 普及组 第 7 页 共 7 页 胜的情况中,小涵最终选出的武将组合的最大默契值。 如果不存在可以让小涵获胜的选将顺序,则输出 0。 【输入输出样例 1】 sanguo.in sanguo.out 6 5 28 16 29 27 23 3 20 1 8 32 26 33 11 12 1 32 【输入输出样例说明 1】 首先小涵拿走 5 号武将;计算机发现 5 号武将和剩下武将中的 4 号默契值最高,于是拿走 4 号;小涵接着拿走 3 号;计算机发现 3、5 号武将之一和剩下的武将配对的所有组合中,5 号和 1 号默契值最高,于是拿走 1 号;小涵接着拿走 2 号;计算机最后拿走 6 号。在小涵手里的 2, 3,5 号武将中,3 号和 5 号配合最好,默契值为 32,而计算机能推出的最好组合为 1 号和 6 号, 默契值为 27。结果为小涵胜,并且这个组合是小涵用尽所有方法能取到的最好组合。 【输入输出样例 2】 sanguo.in sanguo.out 8 42 24 10 29 27 12 58 31 8 16 26 80 6 25 3 36 11 5 33 20 17 13 15 77 9 4 50 19 1 77 【数据范围】 对于 40%的数据有 N≤ 10。 对于 70%的数据有 N≤ 18。 对于 100%的数据有 N≤ 500

思路

其实根据样例不难发现,这道题无论如何都是人赢,但是难度在于如何求出默契值;通过对题目的理解,不难发现,当人选择一个人物时,电脑都会选择默契值最大的人,所以我们只要选择默契值第二大的就可以了,代码如下:

代码

#include #include #include #include #include using namespace std; int n; int a[1005][1005]; int main(){ cin>>n; for(int i=1;i<n;i++){ for(int j=i+1;j<=n;j++){ cin>>a[i][j]; a[j][i]=a[i][j]; } } int ans=0; for(int i=1;i<=n;i++){ sort(a[i]+1,a[i]+1+n); ans=ans>a[i][n-1]?ans:a[i][n-1];//这里是要记录我们选择的默契值里最大的一个 } cout<<1<<endl<<ans;

}

看到这里了,点个赞关注下吧 ♥