20190308搜索测试

几道题都不是很难,但考得也不是很理想,还是做的题不够多啊…

题目

1.简单题
2.促销
3.黑洞
4.激光通讯
5.流星雨

1.简单题

题目描述:

给定数轴上的N个点,求这些点到其他点的距离总和S。

输入格式:

第一行一个整数N。 接下来N行每行一个整数,表示每个点的坐标xi。

输出格式:

一个整数S。

样例数据:

input1:

5
1
5
3
2
4

output1

40

input2

8
10
20
30
40
50
60
70
80

output2

1680

数据规模与约定:

对于30%的数据,1≤N≤10000
对于50%的数据,保证1≤xi、
对于70%的数据,保证1≤N≤300000,且任意两点的坐标不相同。
对于100%的数据,保证1≤N≤3000000,|xi|≤2^15−1。


的确是一道水题,递推着就出来了。

#include<bits/stdc++.h>
using namespace std;
long long n,a[3000101];
long long sum=0;
int main()
{   freopen("cowdis.in","r",stdin);
    freopen("cowdis.out","w",stdout);
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n-1;i++)
	{
		sum=(a[i+1]-a[i])*(i*(n-i)*2)+sum;
	}
	printf("%lld",sum);
}

然而数据范围有点小大,双重循环暴力必然是会TLE的;既然求的是所有点到其他点的距离之和,先排序,算出每个相邻的边被计算了多少次累加起来就可以了;sum+=(a[i+1]-a[i-1])(i(n-i)2),这条边从左边算了i(n-i)次,从右边算也是i*(n-i)次,所以第i条边就一共算了(i*(n-i)*2)次。
坑点:用int就会爆,long long才能过。

2.促销

题目描述:

小x在jzyz开了一家冷饮店,因为刚开张,他决定做一次促销活动。而活动的获奖者可以免费得到一杯圣代。 为了和同学的生活贴近,小x费劲脑汁想到了一个促销方案:
1) 当场摇出一个正整数M,再摇出两个正整数X和Y
2) 每个人可以在1…M1…M这M个自然数中挑出N个不同的数。
3) 如果某人可以在现场把这N个数的倒数的累加和恰好等于X/Y,那么这个人可以得到一杯圣代。
4) 每种方案只限第一名想到答案的领取一杯圣代。

输入格式:

四个整数:N,M, X, Y。

输出格式:

一个整数,表示最多要准备多少杯圣代。

样例数据:

input

2 4 3 4

output

1

数据范围与约定:

对于30%的数据,1<=N<=M<=15
对于60%的数据,1<=N<=M<=20
对于100%的数据,1<=N<=M<=25,X<=25,Y<=25


其实就是给定n,m,x,y,使得从1~m个正整数中选取n个使得这些数的倒数之和=x/y,问有多少种取法。
因为是分数,本蒟蒻考虑的是精度问题,当然qt大佬用的分子分母,约分通分的方法也是可行的。
代码如下:

#include<bits/stdc++.h>
using namespace std;
const double p=0.0000005;//精度标准
double q,x,y;
int n,m,summ=0;
void dfs(int num,double sum,int o)//num表示已经取了几个数,sum表示这些数的倒数之和,o表示前面已经到了第几个数
{   
	if(num==n) 
	{
		if(q+p>=sum&&q-p<=sum) summ++;//判断两个double类型的变量是否相等:abs(a-b)<=0.0000005,a=b;,当然可以根据题目调节精度
		return;
	}
	if(sum>q) return;
    if(o>m) return;
	for(int i=o+1;i<=m;i++)//从下一个数开始选
	{
		dfs(num+1,sum+1.0/i,i);
	}
}
int main()
{   freopen("cd.in","r",stdin);
    freopen("cd.out","w",stdout);
	scanf("%d%d",&n,&m);
	cin>>x>>y;
    q=1.0*x/y;
    dfs(0,0.0,0);
	printf("%d\n",summ);
}

3.黑洞

题目描述:

李宗泽的爱好是在周末进行物理学实验,但事与愿违,实验将NN个黑洞(2<=N<=12,N为even)(2<=N<=12,N为even)具象化在了他的农场里,每个都有明确的坐标位置。
根据他的计算,李宗泽知道将会形成N/2对连接起来的黑洞。如果黑洞A和B被连成一对,那么任何物体进入黑洞A,将会以进入黑洞A的方向从黑洞B中出来;
进入黑洞B,也会以进入时的方向从黑洞A中出来。举例来说,黑洞A在(0,0)(0,0),黑洞B在(1,0)(1,0),牛玉鑫从(1/2,0)(1/2,0)开始向X轴正方向移动,进入黑洞B,从黑洞A中出来,将继续向X轴正方向移动,再次进入黑洞B被困在一个循环里。
李宗泽知道每一个黑洞在他的农场上的具体坐标,牛玉鑫只会向X轴正方向移动,但却不知道牛玉鑫目前的位置。
请你帮助李宗泽计算共有多少种黑洞配对方法会使在不幸的位置的牛玉鑫陷入循环。

输入格式

第一行:一个正整数N;
第二到N+1N+1行:每行两个整数X,Y描述一个黑洞的位置,每个坐标在0…1,000,000,000内。

输出格式:

一个数,代表所有的会让牛玉鑫陷入循环的黑洞配对方法数。

样例数据:

input

4
0 0
1 0
1 1
0 1
有4个黑洞,形成一个正方形的循环。

output

2
注释:给这4个黑洞编号为1…4。如果将1和2相连,3和4相连,牛玉鑫从1和2之间或3和4之间出发时会陷入循环。相同的,如果连接1和3,2和4,牛玉鑫也会陷入循环。只有连接1和4,2和3,牛玉鑫从任何一个位置开始移动都不会陷入循环。

数据范围与约定:

时间限制:1s
空间限制:256MB


点数不多,显然是暴力枚举所有的配对方案进行验证求可行方案总数;
解决以下几个问题:
1)方案一共多少?
N个虫洞保持原顺序不变,我们要配出N/2对来。
    第一对,我们定为1号虫洞和任意另一个虫洞,共N-1种;
    第二对,我们定为剩下的虫洞中编号最小的虫洞和任意另一个虫洞,共N-3种;
    第三对,同理,共N-5种……
    以此类推,直至配出N/2种。我们来数一数,配对总方案数Sum=(N-1)×(N-3)×…×3*1,取N最大值12,则有Sum最大值10395,并不大。
2)如何判断有环?
预处理出每个点与它同行的,右边的,离它最近的点
根据配对方案,看是否能一直走下去,走了N次后,还可以一直走下去,这种方案肯定是可行的。
代码如下:

#include<bits/stdc++.h>
using namespace std;
int n,sum=0;
int next[20]={0},f[20]={0};//next为第i个黑洞右边最近的黑洞,f为第i个黑洞所连接的黑洞
struct fuc
{
	int x,y;
}a[15];
bool mycmp(fuc a,fuc b)
{
  return a.x<b.x||(a.x==b.x&&a.y<b.y);//行小的放前面,行相同的按列数放	
}
bool check()//判断是否有循环 
{
  for(int i=1;i<=n;i++)//枚举每一个起点,只要有一个起点满足方案数+1
  {
    int t=i;
    int vis[20]={0};//一开始的点不能赋值为1(即vis[i]=1),因为既然是向右出发,不通过i,否则就要传送与i配对的黑洞 
	while(next[t])//向右最近的黑洞 
	{
		if(vis[next[t]]) return 1;//循环(黑洞重复经过) 
		vis[next[t]]=1;
		t=f[next[t]];//传送至连接的黑洞 
    }  	
  }	
  return 0;
}
void dfs(int now)//已经搭配到第几个黑洞
{ 
  if(now==n+1)//搭配完了 
  {
    if(check()) sum++;//存在循环 
	return;	
  }
  if(f[now]) dfs(now+1);//目前的黑洞有配对的黑洞了,搜索下一个黑洞 
  else
  {
  	for(int i=now+1;i<=n;i++)
  	{ 
  	  if(!f[i])//没有匹配过 
		{
			f[now]=i;//互相连通 
			f[i]=now;
			dfs(now+1);
			f[now]=f[i]=0;//回溯
		}	
	}
  }
} 
int main()
{ freopen("wormhole.in","r",stdin);
  freopen("wormhole.out","w",stdout);
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
    scanf("%d%d",&a[i].y,&a[i].x);//按照坐标来读入 (y是列,x是行)
  sort(a+1,a+n+1,mycmp);
  for(int i=1;i<=n-1;i++)
    if(a[i].x==a[i+1].x) next[i]=i+1;//预处理出每个黑洞向右的最近的一个黑洞
  dfs(1);
  printf("%d",sum);	
} 

4.激光通讯

题目描述:

海亮教育园的oiers有了新的通讯工具,采用最先进的系统——激光通讯系统,这样可以让他们在整个校园无延时的进行通讯。
海亮教育园校园被设计为由W∗H个点组成的网格。(1<=W<=100;1<=H<=100)这套系统要求类似视线连通以确保维持通讯,当然了,校园里还有一些建筑和树,这些东西会干扰通讯,但是oiers早有准备,他们购买了一些斜放的反光镜(如下图中的"/“和”"),这些镜子能通过反射把激光束扭转90度,下面是问题的一个图解。 在这个地图中H=8,W=7,正在通讯的两名oiers用符号"C"表示,可通讯区域用".“表示,障碍物用”*"表示:
20190308搜索测试

输入格式:

一个正整数M 接下来M行,每行3个整数Xi,Yi和Ti。分别表示第i颗流星落下的坐标和时间。保证所有坐标都在第一象限。

输出格式:

一行,一个整数M,即反光镜的个数。

样例数据:

input

. . . . . . .
. . . . . . C
. . . . . . *
** * * * . *
. . . . * . .
. . . . * . .
. C . . * . .
. . . . . . .

output

3

数据规模与约定:

30% w,h<=30.
50% w,h<=50.


迷宫类题目,但不是求路径而是求起点到终点的最少转弯个数
显然是bfs然而我果断dfs然后完美TLE了
dfs代码:

#include<bits/stdc++.h>
using namespace std;
int minn=99999999;
int w1=0,w2=0,n,m,z1=0,z2=0;
char ch;
int a[110][110],vis[110][110]={0};
const int dx[5]={0,1,0,-1};//方向数组
const int dy[5]={1,0,-1,0};
void dfs(int x,int y,int sum,int pos)//pos表示左右或者上下 ,0表示左右,1表示上下
{
	if(sum>minn) return;
	if((x==w2)&&(y==z2))//到达终点
	{
		minn=min(minn,sum);
		return;
	}
	for(int i=0;i<=3;i++)
	{ int xx=x+dx[i];
	  int yy=y+dy[i];
	  if(vis[xx][yy]) continue;//该点已经被访问过就跳过
	  if((xx>m)||(yy>n)||(xx<=0)||(yy<=0)||(a[xx][yy]==1))  continue;
	  if(pos==-1)//未初始化 (起点)
	  { 
	    vis[xx][yy]=1;
	  	if((i==0)||(i==2)) dfs(xx,yy,sum,0),vis[xx][yy]=0;
	  	if((i==1)||(i==3)) dfs(xx,yy,sum,1),vis[xx][yy]=0;
	  }
	  if(pos==0)//zuoyou
	  { vis[xx][yy]=1;
	  	if((i==1)||(i==3)) dfs(xx,yy,sum+1,1),vis[xx][yy]=0;
	  	else dfs(xx,yy,sum,0),vis[xx][yy]=0;
	  }
	  if(pos==1)
	  { 
	    vis[xx][yy]=1;
	  	if((i==0)||(i==2)) dfs(xx,yy,sum+1,0),vis[xx][yy]=0;
	  	else dfs(xx,yy,sum,1),vis[xx][yy]=0;
	  }
	}
}
int main()
{   freopen("laser.in","r",stdin);
    freopen("laser.out","w",stdout);
	scanf("%d%d",&n,&m);//列 行 
	for(int i=1;i<=m;++i)
	 for(int j=1;j<=n;++j)
	 {  cin>>ch; 
	 	if(ch=='.') a[i][j]=0;
	 	if(ch=='*') a[i][j]=1;
	 	if((ch=='C')&&(w1==0)&&(z1==0)) 
        {  
            w1=i;//起点 
	 		z1=j;
	 	}
	 	if((ch=='C')&&(w1>0)&&(z1>0))
	 	{   
		    a[w2][z2]=0;
	 		w2=i;//终点 
	 		z2=j;
	 	}
	 } 
   dfs(w1,z1,0,-1);
   printf("%d",minn);
}

dfs要求出的是所有的路径来寻找最短的,这样肯定会超时。
bfs思路:
20190308搜索测试
献上bfs代码:

#include<bits/stdc++.h>
using namespace std;
struct fuc
{
	int way;//方向:上下或者左右  1   0(-1表示起点的方向,四个方向拓展不需要转弯)
	int num;//需要放置反光镜的个数,即从起点开始的转弯总数 
	int x,y;//当前的坐标 
}q[100000000];
int n,m,a[110][110],w1,w2,z1,z2;
int vis[110][110];//标记该点有没有经过
char ch;
int main()
{   freopen("laser.in","r",stdin);
    freopen("laser.out","w",stdout);
    scanf("%d%d",&n,&m);//列 行 
	for(int i=1;i<=m;++i)
	 for(int j=1;j<=n;++j)
	 {  cin>>ch; 
	 	if(ch=='.') a[i][j]=0;
	 	if(ch=='*') a[i][j]=1;
	 	if((ch=='C')&&(w1==0)&&(z1==0)) 
        {   w1=i;//起点 
	 		z1=j;
	 	}
	 	if((ch=='C')&&(w1>0)&&(z1>0))
	 	{   
		    a[w2][z2]=0;
	 		w2=i;//终点 
	 		z2=j;
	 	}
	 }
	int head=1,tail=1;
	q[1].way=-1;
	q[1].num=0;
	q[1].x=w1;
	q[1].y=z1;
	int falg;
	for(head=1;head<=tail;head++)
	{
	  int way_to=q[head].way;
	  int xx=q[head].x,yy=q[head].y;
	  vis[xx][yy]=1;
	  if((xx==w2)&&(yy==z2))
	  {
	  	printf("%d\n",q[head].num);
	  	return 0;
	  }
	  for(int i=xx-1;i>=1;i--)//向上延伸
	  { if(a[i][yy]==1) break;//有障碍 
	    falg=0;//不要拐弯 
	    if(way_to==0) falg=1;//原本向左右的需要转弯
	    if(!vis[i][yy])
		{
		 vis[i][yy]=1;
		 q[++tail].x=i;
		 q[tail].y=yy;
		 q[tail].way=1;
		 if(falg) q[tail].num=q[head].num+1;
		 else q[tail].num=q[head].num;	
	    }  	
	  }
	  for(int i=yy+1;i<=n;i++)//向右延伸
	  { if(a[xx][i]==1) break;//有障碍 
	    falg=0;//不要拐弯 
	    if(way_to==1) falg=1;//原本向上下方向的需要转弯
	    if(!vis[xx][i])
		{
		 vis[xx][i]=1;
		 q[++tail].x=xx;
		 q[tail].y=i;
		 q[tail].way=0;
		 if(falg) q[tail].num=q[head].num+1;
		 else q[tail].num=q[head].num;	
	    }  	
	  }
	  for(int i=yy-1;i>=1;i--)//左
	  { if(a[xx][i]==1) break;//有障碍 
	    falg=0;//不要拐弯 
	    if(way_to==1) falg=1;
	    if(!vis[xx][i])
		{
		 vis[xx][i]=1;
		 q[++tail].x=xx;
		 q[tail].y=i;
		 q[tail].way=0;
		 if(falg) q[tail].num=q[head].num+1;
		 else q[tail].num=q[head].num;	
	    }  	
	  }	
	   for(int i=xx+1;i<=m;i++)//右
	  { if(a[i][yy]==1) break;//有障碍 
	    falg=0;//不要拐弯 
	    if(way_to==0) falg=1;
	    if(!vis[i][yy])
		{
		 vis[i][yy]=1;
		 q[++tail].x=i;
		 q[tail].y=yy;
		 q[tail].way=1;
		 if(falg) q[tail].num=q[head].num+1;
		 else q[tail].num=q[head].num;	
	    }  	
	  }	
	}	
} 

5.流星雨

题目描述:

流星雨是美丽的,但是流星落下来也能砸死人的。
有一大片流星要在海亮教育园的操场落下,而小qt恰好在操场数星星。小qt面临最大的问题不是浪漫,而是保住小命。
我们把海亮教育园的操场认为是坐标系的第一象限(以样例解释的图例为准)。小qt现在位于坐标系的原点。
现在有M颗流星会落在海亮教育园的操场上,其中第i颗流星会在时刻T_i砸在坐标为(X_i, Y_i)的格子里。流星的力量会将它所在的格子,以及周围4个相邻的格子都化为焦土,当然小qt也无法再在这些格子上行走,这样他会被烧伤。
小ff从0时刻开始逃离,他只能上下左右移动,并且一个时刻只能移动一个格子,当然,这个格子必须是完好的。
现在小ff想知道,最少经过多少时刻,他可以到达一个安全的格子。

输入格式:

一个正整数M 接下来M行,每行3个整数Xi,Yi和Ti。分别表示第i颗流星落下的坐标和时间。保证所有坐标都在第一象限。

输出格式:

一个整数,表示最少逃离时刻。如果逃离不了,那么输出-1,表示小ff肯定被流星砸着了。

样例数据:

input

4
0 0 2
2 1 2
1 1 2
0 3 5

output

5

【样例说明】:一共有4颗流星将坠落在操场,它们落地点的坐标分别是(0, 0),(2, 1), (1, 1)以及(0, 3),时刻分别为2,2,2,5。
在t=5时的牧场,离小ff最近的安全的格子是(3,0)——不过由于早在第二颗流星落地时,小ff直接跑去(3,0)的路线就被封死了。离小ff第二近的安全格子为(4,0),但它的情况也跟(3,0)一样。再接下来的格子就是在(0,5)-(5,0)这条直线上。在这些格子中,(0,5),(1,4)以及(2,3)都能在5个单位时间内到达。


BFS,先处理每个格子最早被轰的时间,然后BFS时加个判断就可以。
Bfs之前
1)哪些格子是安全的(可以用一个数组记录下最终的地图来判断目前点是否是绝对安全的) 2)处理出每个格子最早被轰的时间。
Bfs中间要做的。
1)拓展四个方向,判断拓展倒的格子的时间和这个格子被轰击的最早时间大小关系,拓展进队。 2)记忆化每个格子是否到过,不然会TLE。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int dx[5]={0,1,0,-1};//方向数组
const int dy[5]={1,0,-1,0};
int n;
struct fuc
{
	int x,y;
	int tim;
}q[100000000];
int vis[310][310],x,y,tim;
int mapp[310][310]={0};//最终的地图,用来判断目前点是否是绝对安全的 
int falg[310][310]={0};//记忆化,标记点有没有到过
int main()
{  freopen("meteor.in","r",stdin);
   freopen("meteor.out","w",stdout);
   memset(vis,1,sizeof(vis));
   scanf("%d",&n);
   for(int i=1;i<=n;++i)
   {
   	 scanf("%d%d%d",&x,&y,&tim);
   	 vis[x][y]=min(vis[x][y],tim);
   	 mapp[x][y]=1;
   	 if(x>=1) vis[x-1][y]=min(vis[x-1][y],tim),mapp[x-1][y]=1;
   	 if(y>=1) vis[x][y-1]=min(vis[x][y-1],tim),mapp[x][y-1]=1;
   	 mapp[x+1][y]=1;
   	 mapp[x][y+1]=1;
   	 vis[x+1][y]=min(vis[x+1][y],tim);
   	 vis[x][y+1]=min(vis[x][y+1],tim);//记录下流星轰击周围每一个点被炸的最早时间
   }
   q[1].x=q[1].y=q[1].tim=0;
   falg[0][0]=1;
   int tail=1;
   for(int head=1;head<=tail;head++)//bfs
   {
   	 int xx=q[head].x;
   	 int yy=q[head].y;
   	 if(!mapp[xx][yy])//此点绝对安全
   	 {
   	   printf("%d",q[head].tim);
	   return 0	;
	 }
	for(int i=0;i<=3;i++)
	{
	  int x_r=xx+dx[i];
	  int y_r=yy+dy[i];
	  if(x_r<0||y_r<0||falg[x_r][y_r]) continue;
	  if(vis[x_r][y_r]>q[head].tim+1)//向四个方向移动,下一时刻不会被炸,拓展 
	  { falg[x_r][y_r]=1;
	    q[++tail].x=x_r;
		q[tail].y=y_r;
		q[tail].tim=q[head].tim+1;  	
	  }	
	} 
   }
   printf("-1");//必然被炸死
   return 0;
} 

这次考试暴露出了太多的问题,基础代码掌握不牢固,思考太片面。还是要多多练习,多多思考,多去请教一下qt大佬。

相关文章:

  • 2022-12-23
  • 2021-07-30
  • 2021-12-19
  • 2021-07-30
  • 2022-12-23
  • 2021-04-29
  • 2021-08-23
猜你喜欢
  • 2021-10-06
  • 2022-02-27
  • 2021-12-14
  • 2021-08-17
  • 2021-09-10
  • 2021-07-31
  • 2022-02-25
相关资源
相似解决方案