暑假2019培训:Day8提高组测试赛

概述

今天又仿佛是场比较欢乐 (fen ji jiao da)的搜索杂赛??
傻笑
cdc出锅第一题,数据说好的1e5却成了5e5,好在后来改了
最后一题我同桌在数据没改之前,超过10就跳出……100分!!100分!!本来要超过15跳的……
在这里插入图片描述

题目顺序

  • 1.朋友圈
  • 2.登峰造极
  • 3.骑士精神

1.朋友圈

(friends.pas/c/cpp)

【背景】

朋友圈有时会传播谣言。

【问题描述】

现在有n个人、m个朋友圈,这n个人的id分别是1-n。大部分人都加入了一个或多个朋友圈,而有的人可能没进朋友圈,有的朋友圈里可能没有人。如果有一个人听到了谣言,他会把这个谣言发布到他加入的所有朋友圈中,所有看到这条谣言的人又会把这条谣言发布到他们所在的朋友圈中,这样谣言就会散布开来。
现在你得到了m个朋友圈中的人员名单,问你对于n个人中的每一个人,如果这个人散播谣言,那么最多会有多少个人听到谣言。

【输入】

第一行两个整数n,m
接下来m行
每行开头是一个整数r,表示这个朋友圈里有多少个人
之后有r个整数,表示这个朋友圈里的人的id
同一行的所有整数用空格隔开

【输出】

一行n个用空格隔开的整数,表示对于id为1,2,…,n的人散播谣言会让最多多少人听到

【输入输出样例1】
f r i e n d s . i n friends.in friends.in

7 5
3 2 5 4
0
2 1 2
1 1
2 6 7

f r i e n d s . o u t friends.out friends.out

4 4 1 4 4 2 2

====================================================

并查集查找,第一题水到不行,改完数据(原本有错)AC人数占将近一半~

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],b[N],r[N],f[N];
int n,m,k,_k;
inline int getfind(int x)
{
	if(x==f[x])return x;
	f[x]=getfind(f[x]);
	return f[x];
}
int main()
{
	freopen("friends.in","r",stdin);
	freopen("friends.out","w",stdout);
	int i,j,x,y;
	for(i=0;++i<=N;)f[i]=i,b[i]=1;
	for(scanf("%d%d",&n,&m),i=0;++i<=m&&scanf("%d",&r[i]);)
	{
		if(r[i]==0)continue;
	    for(scanf("%d",&x),k=getfind(x),j=0;++j<r[i];)
		{
			scanf("%d",&y);
			_k=getfind(y);
			if(_k^k)f[_k]=k,b[k]+=b[_k];
	    }	
	}
	for(i=0;++i<n;)printf("%d ",b[getfind(i)]);
	printf("%d",b[getfind(i)]);
	
	return 0;
}

2.登 峰 造 极

(dfzj.pas/c/cpp)

【背景】

几十年前,一位长者路过鲁迅故里。
看着高兀奇峰,他不禁发出感慨:
登 峰 造 极 !

【问题描述】

从天上看,山峦整齐地排列成n行m列,第i行第j列的山峰高度为h[i][j]。
如果从一个高度为h的山峰开始,在不经过高度小于等于h-d的山峰的前提下,无法到达高度比h高的山峰,那么我们称这座山峰登峰造极。
你需要告诉他,有几座山峰登峰造极。

【输入】

第一行两个空格隔开的整数n和m和d
接下来n行,每行m个空格隔开的整数,表示山峰的高度

【输出】

一行一个整数,含义如题

【输入输出样例1】
d f z j . i n dfzj.in dfzj.in

6 10 2
0 0 0 0 0 0 0 0 0 0
0 1 2 1 1 1 1 0 1 0
0 2 1 2 1 3 1 0 0 0
0 1 2 1 3 3 1 1 0 0
0 2 1 2 1 1 1 0 2 0
0 0 0 0 0 0 0 0 0 0

d f z j . o u t dfzj.out dfzj.out

4

【数据范围】

对于30%的数据:n,m<=10
对于100%的数据:n,m<=500

============================================================

60分很好打,一道简单的暴力搜索
看代码~在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N=505;
const int fx[4]={1,-1,0,0};
const int fy[4]={0,0,1,-1};
int n,m,d,k,ans;
int a[N][N],f[N][N],vis[N][N];
void dfs(int st,int sy,int x,int y)
{
	int i;
    for(f[x][y]=1,i=-1;++i<4;)
	{
	    int nowx=x+fx[i],nowy=y+fy[i];
	    if(f[nowx][nowy])continue;
		if(nowx>n||nowx<0||nowy>m||nowy<0)continue;
	    if(a[nowx][nowy]<=(a[st][sy]-d))continue;
	    if(a[nowx][nowy]<a[st][sy])vis[nowx][nowy]=-1;
	    if(a[nowx][nowy]>a[st][sy])vis[st][sy]=1;
	    dfs(st,sy,nowx,nowy);
	}
}
int main()
{
	freopen("summits.in","r",stdin);
	freopen("summits.out","w",stdout);
	int i,j;
	for(scanf("%d%d%d",&n,&m,&d),i=0;++i<=n;)
		for(j=0;++j<=m&&scanf("%d",&a[i][j]);)a[i][j]==0?vis[i][j]=-1:vis[i][j]=0;
	for(i=0;++i<=n;)
	  for(j=0;++j<=m;)
	    if(!vis[i][j])
	      memset(f,0,sizeof(f)),dfs(i,j,i,j);
	for(i=0;++i<=n;)
	  for(j=0;++j<=m;)
	    if(!vis[i][j])++ans;
	printf("%d",ans);
	return 0;
}

那么100分

能访问到的点是随出发的点的高度减少而增加的,也就是说从高的点出发能访问到的点,从低的点出发一定能访问到
那么从最高的山峰出发,沿途经过的点,除了高度和它一样的,成功登峰造极,剩下的全被日死了
而对于第二高的山,如果bfs的时候发现之前最高的山访问过的节点,那一定也跪了,因为之前能走的现在都能走
第二高遍历的时候经过的点也全完,因为要么高度一样,要么更低

那么,代码就是这样的:std by cdcq:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int rd(){int z=0,mk=1;  char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')mk=-1;  ch=getchar();}
	while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
	return z*mk;
}
int fx[4]={1,-1,0,0},fy[4]={0,0,1,-1};
struct nds{int x,y,z;}b[251000];
int n,m,o,a[510][510];
int q[251000],hd=0;
int vstd[510][510],vtp=0;
int ans=0;
int gtid(int x,int y){  return (x-1)*m+y-1;}
bool cmp(nds x,nds y){  return x.z<y.z;}
bool chck(int x,int y){  return x>=1&&x<=n&&y>=1&&y<=m;}
void bfs(int _x,int _y){
	if(vstd[_x][_y])  return ;
	++vtp;
	q[hd=1]=gtid(_x,_y);  vstd[_x][_y]=vtp;
	bool flg=false;
	for(int k=1;k<=hd;++k){
		int x=q[k]/m+1,y=q[k]%m+1;
		for(int i=0;i<4;++i)
			if(chck(x+fx[i],y+fy[i]) && a[x+fx[i]][y+fy[i]]>a[_x][_y]-o){
				if(vstd[x+fx[i]][y+fy[i]]&&vstd[x+fx[i]][y+fy[i]]!=vtp)  flg=true;
				else if(!vstd[x+fx[i]][y+fy[i]]){
					vstd[x+fx[i]][y+fy[i]]=vtp;
					q[++hd]=gtid(x+fx[i],y+fy[i]);
				}
			}
	}
	if(!flg)
		for(int i=1;i<=hd;++i)
			ans+=(a[q[i]/m+1][q[i]%m+1]==a[_x][_y]);
	return ;
}
int main(){
	//freopen("ddd.in","r",stdin);
	freopen("dfzj.in","r",stdin);
	freopen("dfzj.out","w",stdout);
	memset(vstd,0,sizeof(vstd));
	cin>>n>>m>>o;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			a[i][j]=rd();
			b[gtid(i,j)+1]=(nds){i,j,a[i][j]};
		}
	sort(b+1,b+n*m+1,cmp);
	for(int i=n*m;i>=1;--i)
		bfs(b[i].x,b[i].y);
	cout<<ans<<endl;
	return 0;
}


3.骑士精神

(spirit.pas/c/cpp)

【背景】

这是一道经典题。

【问题描述】

在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘:
在这里插入图片描述
为了体现出骑士精神,他们必须以最少的步数完成任务。

【输入】

第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。

【输出】

对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。

【输入输出样例1】
s p i r i t . i n spirit.in spirit.in

2
10110
0111
10111
01001
00000
01011
110
1
01110
01010
00100

s p i r i t . o u t spirit.out spirit.out

7
-1

============================================================
没有错,这就是你想象中的骑士精神
原题,再次不多做详解了
可以用双向bfs,也可以用IDA*
但不知道,如过双向bfs套IDA会不会更快》》
在这里插入图片描述
佛了
反正这代码长度我可不去打
还是IDA
短~嗯
在这里插入图片描述
代码

#include<bits/stdc++.h>
using namespace std;
const char b[5][5]={{'1','1','1','1','1'},{'0','1','1','1','1'},{'0','0','*','1','1'},{'0','0','0','0','1'},{'0','0','0','0','0'}};
const int dx[8]={-2,-2,-1,-1,1,1,2,2},dy[8]={-1,1,-2,2,-2,2,-1,1};
char a[5][5];
int T,i,cnt;
int ID(int &x,int &y)
{
	int i,j,k=0;
	for(i=-1;++i<5;)
		for(j=-1;++j<5;)
		{
			if(a[i][j]=='*')x=i,y=j;
			if(a[i][j]!=b[i][j])++k;
		}
	return k;
}
bool dfs(int _x)
{
	int _y,x,y,k;
	_y=ID(x,y);
	if(_x+_y-1>cnt)return 0;
	if(!_y)return 1;
	for(k=0;k<8;k++)
	{
		int nowx=x+dx[k],nowy=y+dy[k];
		if(nowx<0||nowx>4||nowy<0||nowy>4)continue;
		swap(a[x][y],a[nowx][nowy]);
		if(dfs(_x+1))return 1;
		swap(a[x][y],a[nowx][nowy]);
	}
	return 0;
}
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	scanf("%d",&T);
	while(T--)
	{
		for(i=-1;++i<5;)scanf("%s",a[i]);
		for(cnt=0;++cnt<=15&&!dfs(0););
		if(cnt>15)puts("-1");
		else printf("%d\n",cnt);
	}
	return 0;
}

--------在这里插入图片描述

结束~~

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值