暑假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
1101
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;
}
--------