【纪中2020.2.25日】模拟赛题解

目录:

T1:朋友
T2:分数统计
T3:跳棋
T4:迷宫大门

还好这次题目不考题目理解……

正题:

T1:朋友

题目描述

经过六年的努力,小明终于被一所知名中学录取。优秀的小明总是对一些奇奇怪怪的事情感兴趣,这次他想知道谁在这所新学校拥有的朋友最多,由于大家都才刚报到,所以小明只知道大家两两之间是否是朋友关系。

输入

输入文件friend.in的第一行有两个整数n和m,n表示总人数,m表示总关系数。
接下来m行,每行有2个以空格隔开的整数a和b,表示a和b是朋友,a和b均为1到n之间的整数。不会给出重复的朋友关系。

输出

输出文件friend.out中仅有一行,表示朋友数最多的人所拥有的朋友,每两个整数之间用空格隔开,按照字典序从小到大输出。如果存在多个人朋友数都是最多的情况,请输出字典序最小的那人的答案,具体见样例。

样例输入

3 3
1 2
2 3
1 3

样例输出

2 3

分析:

这道题暴力可以拿80分,很不错的。正解是图论||更巧妙的枚举
一道基础的图论题||思考题暴力思路:直接暴力记录AC思路:用的是高效的统计,开一个int 数组f。f[i]表示i朋友的数目,读入a,b ;f[a],f[b]都自增1,最后找出最大且序号最小的i

AC CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
long long n,m,b[500005],maxx,minn,s[500005],t[500005],k,c[1000005];
int main()
{
	freopen("friend.in","r",stdin);
	freopen("friend.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&s[i],&t[i]);
		b[s[i]]++;  //自增1
		b[t[i]]++;
	}
	for(int i=1;i<=n;i++)
	{
		if(b[i]>maxx) maxx=b[i],minn=i;  //找最大值,记录最大值位置
	}
	for(int i=1;i<=m;i++)
	{
		if(s[i]==minn)  //最大且序号最小
			k++,c[k]=t[i];
		if(t[i]==minn)
			k++,c[k]=s[i];
	}
	sort(c+1,c+k+1);  //题目要求字典序
	for(int i=1;i<=k;i++)
	{
		printf("%lld ",c[i]);  //输出
	}
	return 0;
}

T2:分数统计

题目描述

在统计完朋友情况之后,小明又对大家的毕业学校产生兴趣,但是他觉得单纯的统计人数是一件非常无聊的事情,于是他设计了一个算法,同一所学校毕业的学生,第1个将获得1分,第2个获得2分,第3个获得4分…,第i个将获得2^(i-1)分,总分就是这所小学的得分,小明想知道得分最高的学校有多少分。

输入

输入文件score.in的第一行有两个整数n和m,n表示总人数,m表示已知的同校关系数量。
接下来m行,每行有2个以空格隔开的整数a和b,表示a和b是来自同一所学校,a和b均为1到n之间的整数。不会给出重复的信息。

输出

输出文件score.out只有一行,为所有学校中的最高得分。最后得分可能会很大,你只需要输出后100位即可,不足100位的请直接输出。

样例输入

5 3
1 2
3 4
1 3

样例输出

15

分析:

这道题比较复杂数据规模大,需要用到高精度
算法部分用到了并查集,以及还要输出后100位
这种又臭又长的代码中想AC细节details是最需要注意的。

CODE:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,i,j,l,x,y,len,rootx,rooty,maxx,f[100001],ans[100001],a[100001];
int getfather(int x)  //找爸爸(祖先节点)函数
{
	if(f[x]==x)
		return x;
	else
	{
		f[x]=getfather(f[x]);
		return getfather(f[x]);
	}
}
int main()
{
	freopen("score.in","r",stdin);
	freopen("score.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)
		f[i]=i;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		rootx=getfather(x);  //并查集
		rooty=getfather(y);  //每次找出x,y的祖先节点
		f[rootx]=rooty;
	}
	for(i=1;i<=n;i++)
		f[i]=getfather(i);  //每次找祖先节点
	for(i=1;i<=n;i++)
		ans[f[i]]++;
	for(i=1;i<=n;i++)
		if(ans[f[i]]>maxx)
			maxx=ans[f[i]];  //找最大值
	a[1]=1;
	l=1;
	for(i=1;i<=maxx;i++)  
	{
		x=0;
		for(j=1;j<=l;j++)
		{
			a[j]=a[j]*2+x;
			x=a[j]/10;
			a[j]=a[j]%10;
		}
		while(x>0)  //高精
		{
			l++;
			a[l]=x%10;
			x=x/10;
		} 
	}
	a[1]--;
	if(l>=100)  //后100位
		len=100;
	else
		len=l;
	for(i=len;i>=1;i--)  //需要倒序输出每位
		printf("%d",a[i]);
	return 0;
}

T3:跳棋

题目描述

小明迷恋上了一个新的跳棋游戏,游戏规则如下:棋盘是一排从0开始,顺序编号的格子,游戏开始时你位于0号格子,你每次只能往编号大的格子跳,而且你每次至少需要跳过L个格子,至多只能跳过R个格子。每个格子都有一个给定的伤害值,显然你希望得到的伤害值越少越好。
你能告诉小明他当他跳到最后一个格子时受到的累积伤害值最小为多少吗?
如果无论如何小明都无法跳到最后一个格子,这个时候你需要输出”-1”。
注:从i号格子跳过x个格子表示从i号格子跳到第i+x+1号格子。

输入

输入文件jump.in第一行有三个整数n、L和R,n表示格子的编号从0到n。L和R表示最少需要跳过的格子数和最多能够跳过的格子数。
第二行有n个正整数,两个数字间用空格隔开,表示每个格子的伤害值。

扫描二维码关注公众号,回复: 9853203 查看本文章

输出

输出文件jump.out仅有一个整数,表示受到的最小伤害值,保证结果小于maxlongint。

样例输入

10 2 6
1 3 5 7 9 2 4 6 8 10

样例输出

12

提示

在这里插入图片描述

分析:

这道题是一道dp,但单单是dp是不行的,要考虑优化
我们可以用单调队列线段树优化。

单调队列优化CODE:

#include<cmath>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
int n,i,l,r,head,tail;
LL q[1000001],a[1000001],f[1000001];
int main(){
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	scanf("%d%d%d",&n,&l,&r);
	for(i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		f[i]=0x7fffffff;  //防止溢出,设置无限大
	}
	head=1;f[0]=0; //单调队列初始化为空
	for(i=1;i<=n;i++)
	{
		if((i-l-1)>=0)  //至少大于左边界
		{
			while(head<=tail&&f[q[tail]]>=f[i-l-1])
			tail--;  //剔除不可能
			tail++;
			q[tail]=i-l-1;
		}
		while(head<=tail&&q[head]<(i-r-1)) head++;
		if(head<=tail) f[i]=a[i]+f[q[head]];  //队列不为空
	}
	if(f[n]<0x7fffffff)
	printf("%lld\n",f[n]);
	else cout<<"-1"<<endl; //永远跳不到
return 0;
}

T4:迷宫大门

题目描述

在跳棋游戏大获全胜后,小明就开始一个人在校园里溜达了。突然他在校园角落里发现了一面神奇的墙壁,墙壁上有一排钉子,每个钉子上都挂着一根两端系有小球的绳子,如下图所示
在这里插入图片描述
小明可以调整每一根绳子在钉子左右两端的长度,当来自不同绳子的相邻小球高度一样时(具体可见样例说明),便可获得积分1分。当小明的方案获得最高积分时,迷宫大门就会开启,小明就可以进去寻找宝藏啦!

输入

输入文件door.in第一行为一个正整数n,表示墙上的绳子数。
接下来n行,每行2个整数a和b,表示绳子左右两端的初始长度。

输出

输出文件door.out仅有一个正整数,表示小明可以获得的最高积分。

样例输入

3
1 1
3 2
1 4

样例输出

2

提示:

在这里插入图片描述

分析:

这道题正解贪心,但很多人模拟赛时没做出来,是有一定难度的。
首先我们每次记录一组x和y
分别表示前一个位置所能接受的的最小高度和最大高度
用一个数组存和
一开始minn=0,maxx=a[1] //a[i]表示第i个位置绳子长度的总和
2 to n
如果a[i]<minn,则无法接受,将minn更新为0,maxx更新为s[i]即可
否则
累加答案并更新

(以下要求全文背诵)
如果a[i]>maxx (新)minn=a[i]-maxx
要么 (新)minn=0
(新)maxx=a[i]-(目前)minn
minn=(新)minn
maxx=(新)maxx
累加ans

CODE:

#include<cmath>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long LL;
int i,l,r,x,y,n,maxx,minn,ans;
int a[500001];
int main(){
	freopen("door.in","r",stdin);
	freopen("door.out","w",stdout);
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d%d",&x,&y);
		a[i]=x+y;  //记录和
	}
	maxx=a[1];
	for(i=2;i<=n;i++)
	{
		if(a[i]<minn){  //无法接受
			minn=0;
			maxx=a[i];
		}
		else{  //累加并更新
			if(maxx<a[i]) l=a[i]-maxx;
			else l=0;
			r=a[i]-minn;  //分别更新min与max
			minn=l;
			maxx=r;
			ans++;  //累加
		}
	}
	printf("%d",ans);	
return 0;
}

发布了53 篇原创文章 · 获赞 37 · 访问量 5432

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/104545189