【NHOI2019】初中组区赛解题思路

第一题:候选人

有 N 头奶牛为偶像比赛试镜。你是比赛的初级制作人。在试镜期间,您已经评估了每头奶牛的天赋和技能。对于每个有效的 i,talent [i]是天赋,skill[i]是技巧。你的任务是决定哪些奶牛进入选拔的第二阶段。为了做到这一点,你提出了一个简单的规则:

如果不存在奶牛 B,使得奶牛 B 的天赋和技巧都比奶牛 A 高,那么奶牛 A 就能进入第二轮。请计算能进入第二阶段的奶牛的数量。

【输入格式】

第一行,一个整数 N。2 <= N <= 50。

接下来有 N 行,第 i 行是: talent [i]、skill[i]。1 <= talent [i], skill[i] <= 10000。

【输出格式】

一个整数。

【输入样例】

3

10 30

20 20

30 10

【输出样例】

3

解题思路:首先看到这题的数据范围如此小,而且是第一题,就不用想这么复杂的方法了,直接暴力枚举就可以通过了;即用当前奶牛的天赋和才能与其它奶牛的天赋与才能进行比较即可得到正确答案。时间复杂度O({n^{2}})最多为50*50次,很明显枚举不超时,直接枚举通过。

这里要注意的是双重循环的其实条件:外层循环是0~n即第一个数据到最后一个,而内层循环也应该是0~n,因为是要将第一个与其余的所有数据进行比较,注意内层循环不要写成i+1~n。附代码如下:

#include  
using namespace std;
struct cow{
	int talent,skill;
}a[55];
int n,Wrong,ans;
int main(){
	cin>>n;
	for(int i=0;i>a[i].talent>>a[i].skill;
	for(int i=0;i

第二题:线段覆盖

在一条数轴上,有 N 条线段,第 i 条线段的左端点是 s[i],右端点是 e[i]。如果线段有重叠

(即使是端点重叠也算是重叠),则输出“impossible”, 如果没有重叠则输出“possible”。

【输入格式】

多组测试数据。

第一行,一个整数 G,表示有 G 组测试数据。1 <= G <= 10。每组测试数据格式如下:

第一行,一个整数 N。 1 <= N <= 10。

接下来有 N 行,每行两个整数:s[i],e[i]。 0<=s[i],e[i]<=1000000。

【输出格式】

共 G 行,每行一个字符串,不含双引号。

【输入样例】

5

3

10 47

100 235

236 347

3

100 235

236 347

10 47

2

10 20

20 30

3

10 20

400000 600000

500000 700000

4

1 1000000

40 41

50 51

60 61

【输出样例】

possible

possible

impossible

impossible

impossible

解题思路:首先思考,两段线段之间的情况,只有①重合和②相离两种情况

第一种情况:两线段重合(注相邻也算重合)

如:

第一条:10 20

第二条:20 30

这样算两线段重合

第二种情况:两线段相离

如:

第一条:10 20

第二条:21 30

这样算两线段相离

因此,直接定义结构体,结构体里面有开始和结束两个变量,以先按结束位置排序,如果结束位置相同的话,则按起始位置排序。附代码如下:

#include  
using namespace std;
struct Node{
	int st,end;
}a[1000005];
bool cmp(Node a,Node b)
{
	if(a.end==b.end)//结束位置相同的话,按起始位置排序 
		 return a.st>G;
	for(int i=0;i>n;
		for(int j=0;j>a[j].st>>a[j].end;
		sort(a,a+n,cmp);
		for(int j=0;j=a[j+1].st) 
			{
				sign=true;
				break;
			}
		if(sign==true)
			cout<<"impossible"<

第三题:海滩

给你一个N×M 的矩阵,'.'表示海水,'#'表示陆地。每个海水的形状都是正六边形,每个陆地也是正六边形。海水与陆地的公共边就是海滩。

矩阵的偶数行的图形有些许偏右,请看样例的图。请输出海滩的个数。输入格式:

第一行:N 、M。 1 <= N,M <= 50.

接下来是N 行M 列的矩阵。输出格式:

一个整数,海滩的数量。

样例输入:

3 6

..#.##

.##.#.

#.#...

样例输出:

19

样例解释:

海滩用红色表示。

【NHOI2019】初中组区赛解题思路_第1张图片

解题思路:刚看到题首先想到的是先求出连通块的位置,然后再判断连通块的位置,但是做了一下,发现有点复杂;想它第三题应该不会出这么难的题目;于是换了一个思路,六边形旁边有水的地方就会形成海滩,然后再看判断六边形边界的时候,奇数行和偶数行判断边界有点不同,

奇数行:在当前位置是陆地的前提下判断上、左上、左边、右边、下、左下的六边形是否为海洋即可;

偶数行:在当前位置是陆地的前提下判断上、右上、左边、右边、下、右下的六边形是否为海洋即可;下面的样例,判断后得出答案为19.

..#.##

.##.#.

#.#...

附代码如下:

#include 
using namespace std;
char a[55][55];
int n,m,ans;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			if(i%2==1) 
			{
				if(a[i][j]=='#')//判断奇数行的六边形六条边是否有陆地相邻 
				{
					if(a[i-1][j-1]=='.')
						ans++;
					if(a[i-1][j]=='.')
						ans++;
					if(a[i][j-1]=='.')
						ans++;		
					if(a[i][j+1]=='.')
						ans++;										
					if(a[i+1][j-1]=='.')
						ans++;
					if(a[i+1][j]=='.')
						ans++;			
				}
			}
			else
			{
				if(a[i][j]=='#')//判断偶数行的六边形六条边是否有陆地相邻 
				{
					if(a[i-1][j]=='.')
						ans++;
					if(a[i-1][j+1]=='.')
						ans++;
					if(a[i][j-1]=='.')
						ans++;			
					if(a[i][j+1]=='.')
						ans++;									
					if(a[i+1][j]=='.')
						ans++;
					if(a[i+1][j+1]=='.')
						ans++;			
				}			
			} 
		} 
	cout<

第四题:货币系统

【题目描述】

奶 牛 王 国 现 有 货 币 系 统 的 银 币 面 值 有

1,2,5,10,20,50,100,200,500,1000,2000,5000,10000,20000,50000。

现在农夫打算在现有货币系统的基础上,再推出一种新面值:newBanknote。  有 N 头奶牛要购物,第 i 头奶牛购买的商品的价值是 cost[i]。

现在你要回答 N 个问题,第 i 个问题是:至少需要多少枚银币才能恰好凑成 cost[i]?

【输入格式】

第一行,两个整数:newBanknote 和 N。1 <= newBanknote <= 2000000000。 1<=N<=50。第二行,N 个整数,第 i 个整数是 cost[i]。1 <= cost[i] <= 2000000000。

【输出格式】

共一行,N 个整数,依次对应 N 个问题的答案。

【输入样例】

4700 4 

53 9400 9401 30000

【输出样例】

3 2 3 2

解题思路:看到题目,首先想到的是使用贪心,将输入的面值和原来的面值从小到大排序;然后循环判断需要多少张纸币;这种思路连样例都不能通过,样例中有9400这个值,直接贪心求出需要5000+2000+2000+200+200=5张纸币,但如果使用新面值则需要两张纸币而已,4700+4700=2张纸币。

仔细看货币原来15种货币面值,存在关系:当中的某种货币面值等于前面几种连续面值的若干张货币相加。因此,可以先与新面值比较判断再贪心与原来面值的比较即可。附代码如下:

#include 
using namespace std;
long long a[20]={1,2,5,10,20,50,100,200,500,1000,2000,5000,10000,20000,50000},nc,n,cost,cost2,ans,t;
int main(){
	cin>>nc>>n;
	for(int i=0;i>cost;
		ans=2000000000;
		for(int j=0;j<=50000;j++)//枚举新面值的所有情况 
		{
			cost2=cost;//注意:每次循环前初始化 
			t=0;
			if(j*nc>cost)//如果枚举的花费大于物品的花费,则不需要继续枚举 
				break;
			t=j;//记录当前这种情况需要多少张硬币
			cost2=cost2-j*nc;
			for(int k=14;k>=0;k--)//从面值大到小贪心枚举原来面值的所有情况 
			{
				t=t+cost2/a[k];
				cost2=cost2%a[k];
			}	
			ans=min(ans,t);			
		}
		cout<

第五题:蜈蚣

【题目描述】

你有 C 只蜈蚣,每只蜈蚣有 F 只脚。冬天来了,要给蜈蚣们穿袜子。抽屉里有 N 种颜色的袜子,第 i 种颜色袜子的数量有 a[i]只。对于一只蜈蚣来说,它所有的脚穿的袜子的颜色必须相同。现在你闭上眼睛,从抽屉里面随意拿出 X 只袜子,你要保证随意拿出来的 X 只袜子一定可以满足所有蜈蚣的需求。那么 X 的最小值是多少?如果 X 不存在,输出-1。

【输入格式】

第一行,三个整数,C,F,N。1<=C<=50,1<=F<=100,1<=N<=100。

第二行,N 个整数,第 i 个整数是 a[i]。1 <= a[i] <= 10000000。

【输出格式】

一个整数。最小的 X,如果 X 不存在,输出-1。

【输入样例】

1 100 5

1 1 1 1 100

【输出样例】

104

解题思路:看见这道题目首先想到了使用贪心模拟,即
(1)当该种颜色的袜子数大于等于蜈蚣的脚数,就加上蜈蚣脚数-1

(2)当该种颜色的袜子数小于蜈蚣的脚数,就加上这种颜色的袜子数

认为这样就是最坏的情况,但是提交后发现做错了,

附错误代码如下:

#include  
using namespace std;
int c,f,n,a[105],ans,maxi;//maxi记录剩余袜子中最多颜色的袜子下标 
int main(){
	cin>>c>>f>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		if(a[i]>=f)
		{
			ans+=f-1; 
			a[i]-=f-1;
		}
		else//a[maxi])
			maxi=i;
	}
	if(a[maxi]==0)//最多的袜子多没有剩余 
		cout<<-1;
	else
	{
		a[maxi]-=c;
		ans+=c;//穿好一只蜈蚣的袜子 
        cout<

我们来看一个反例:

2 3 3
4 4 5 

假如我们使用上面的思路,得出2+2+2+1+1=8,即最坏情况每种颜色的袜子先拿两只,最后随便拿2只;

但实际这样并不是一定符合要求的情况,我可以先从三种颜色的袜子中拿3-1=2只;然后,从剩下袜子中最多的中拿一只,穿好第一只蜈蚣的袜子;判断是否可以从剩下最多的那一堆再拿3-1=2只,发现可以,则再从第三堆拿2;然后更新当前最多数量的袜子堆数下标,最后再从剩余袜子最多的一堆中拿1只,穿好第二只蜈蚣的袜子;这样要拿(3-1)+(3-1)+(3-1)+1+(3-1)+1=10次;因此发现原来的思路还有考虑不全的地方。

正确的思路是:一条蜈蚣的时候先按照原来的贪心判断,如果蜈蚣数量大于等于2,则从第二次开始每次用剩余最多的颜色袜子数量按原来的思路判断,然后更新,标记的最多袜子的下标然后从剩余袜子最多的一堆中拿1只,如此循环拿。这题的考点是贪心原理,附代码如下:

#include  
using namespace std;
int c,f,n,a[105],ans,fuhe;

int getMaxi() 
{
	int maxi=0; 
	for(int i=1;i<=n;i++)
		if(a[i]>a[maxi])
			maxi=i;
	return maxi; 
}

int main(){
	cin>>c>>f>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	int maxi=0;//记录剩余袜子中数量最多的袜子颜色下标 		
	for(int i=1;i<=c;i++)		//枚举1~c条蜈蚣的情况 
	{
		if(i==1)//第一条蜈蚣特判 
			for(int j=1;j<=n;j++)
			{
				if(a[j]>=f-1)
				{
					ans+=f-1; 
					a[j]-=f-1;//拿完袜子后 
				}
				else//=f-1) 
			{
				ans+=f-1;
				a[maxi]-=f-1;
			}
			else
			{
				ans+=a[maxi];
				a[maxi]=0;
			}
			maxi=getMaxi();//获取当前剩下中最多的 
		} 
		if(a[maxi]==0)//如果剩余最多的袜子都为0,无法满足蜈蚣穿
		{
			cout<<-1;
			return 0; 
		} 
		a[maxi]--;//先穿好一只蜈蚣,袜子数量减1 
		ans++; //拿的次数加1 
	}
	cout<

第六题:珠宝

【题目描述】

Elly 有一个狭长的珠宝盒。在盒子里,她储存了一排 N 颗珍珠。每颗珍珠的颜色是 M 种不同的颜色的其中一种,第 i 颗珍珠的颜色是 c[i]。颜色相同的珍珠要排在相邻。最终配置中颜色的顺序并不重要。例如,Elly 并不关心白色珍珠是在粉红色珍珠之前还是之后,但所有白色珍珠都必须是排在一起,所有粉红色珍珠也必须排在一起。

现在,Elly 想知道她必须移动的珍珠数量是多少才能按颜色分组?(移动珍珠意味着将它从行中取出然后将其插回任意位置 - 在任何两个珍珠之间或在所有珍珠之前或之后。)

【输入格式】

第一行,两个整数:N 和 M。1<=N<=50,1<=M<=15。第二行,N 个整数,第 i 个整数是 c[i]。 1 <= c[i] <= M。

【输出格式】一个整数。

【输入样例】

11 4

2 4 1 1 1 3 2 1 4 2 2

【输出样例】

3

解题思路:看到题目涉及到状态转换的,就想到要使用动态规划的思想,但是应该如何使用动规呢?笔者首先想到的是先找到最终结果,然后使用动态规划的思想,创建一个二维数组f[55][55],而f[i][j]=x表示放到第i颗珍珠的且判断到第j个位置时,和最终数组的最大匹配程度为x;因此,我们可以直接使用n-f[n][n]得出需要移动的最小次数;将原数组与最终数组进行比较,如果

你可能感兴趣的:(C++,结构体,贪心原理,动态规划,枚举模拟)