【习题详解】搜索

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/83653804

搜索习题详解

分数字

题目描述
现有N件不可区分的物品,将它们分成10份,要求每份在1~3件之间,问有多少种方案,并按字典序输出所有方案。
输入格式
一个整数,表示N<=10000N<=10000。
输出格式
第一行一个数M,表示方案数。
接下来M行每行10个整数,表示1种方案。
样例数据
input

11
output
10
1 1 1 1 1 1 1 1 1 2
1 1 1 1 1 1 1 1 2 1
1 1 1 1 1 1 1 2 1 1
1 1 1 1 1 1 2 1 1 1
1 1 1 1 1 2 1 1 1 1
1 1 1 1 2 1 1 1 1 1
1 1 1 2 1 1 1 1 1 1
1 1 2 1 1 1 1 1 1 1
1 2 1 1 1 1 1 1 1 1
2 1 1 1 1 1 1 1 1 1

十重循环,暴力枚举。

#include<bits/stdc++.h>
using namespace std;
int dd[100000][10];
int main()
{
	freopen("allot.in","r",stdin);
	freopen("allot.out","w",stdout);
	int n,ans=0;
	cin>>n;
	for (int a=1;a<=3;++a)
	    for (int b=1;b<=3;++b)
	        for (int c=1;c<=3;++c)
	            for (int d=1;d<=3;++d)
	                for (int e=1;e<=3;++e)
	                    for (int f=1;f<=3;++f)
	                        for (int g=1;g<=3;++g)
	                            for (int h=1;h<=3;++h)
	                                for (int i=1;i<=3;++i)
	                                    for (int j=1;j<=3;++j)
	                                        if (a+b+c+d+e+f+g+h+i+j==n)
	                                        {
	                                        	ans++;
	                                        	dd[ans][1]=a;
	                                        	dd[ans][2]=b;
	                                        	dd[ans][3]=c;
	                                        	dd[ans][4]=d;
	                                        	dd[ans][5]=e;
	                                        	dd[ans][6]=f;
	                                        	dd[ans][7]=g;
	                                        	dd[ans][8]=h;
	                                        	dd[ans][9]=i;
	                                        	dd[ans][10]=j;
	                                        }
	printf("%d\n",ans);
	for (int i=1;i<=ans;++i) 
	    for (int j=1;j<=10;++j)
	        if (j==10) cout<<dd[i][j]<<'\n';
	            else cout<<dd[i][j]<<' ';
	fclose(stdin);fclose(stdout);
	return 0;
}

流星雨

题目描述
流星雨是美丽的,但是流星落下来也能砸死人的。
有一大片流星要在海亮教育园的操场落下,而小x恰好在操场数星星。小x面临最大的问题不是浪漫,而是保住小命。
我们把海亮教育园的操场认为是坐标系的第一象限(以样例解释的图例为准)。小x现在位于坐标系的原点。
现在有M颗流星会落在海亮教育园的操场上,其中第i颗流星会在时刻T_i砸在坐标为(X_i, Y_i)的格子里。流星的力量会将它所在的格子,以及周围4个相邻的格子都化为焦土,当然小x也无法再在这些格子上行走,这样他会被烧伤。
小x从0时刻开始逃离,他只能上下左右移动,并且一个时刻只能移动一个格子,当然,这个格子必须是完好的。
现在小x想知道,最少经过多少时刻,他可以到达一个安全的格子。
输入格式
一个正整数M 接下来M行,每行3个整数Xi,Yi和Ti。分别表示第i颗流星落下的坐标和时间。保证所有坐标都在第一象限。
输出格式
一个整数,表示最少逃离时刻。如果逃离不了,那么输出-1,表示小x肯定被流星砸着了。
样例数据
input

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

题解
这道题问的是逃亡的最小时间,显然求诸如最小距离之类的问题我们都应该选择bfs来完成。
我们先预处理每一个点变成不能经过的点的最小时间,永远安全的点为正无穷。
从起点开始跑广搜,只要扩展节点的时候判断当前实现经过该节点是否安全即可。
CODE

#include<bits/stdc++.h>
using namespace std;
#define maxn 400
#define maxm 60000

struct stars{
	int x,y,t;
}star[maxm];
int m,flag=1;
int v[maxn][maxn];
int d[maxn][maxn];
int danger[maxn][maxn];
int qx[maxn],qy[maxn];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};

inline void read(int &readnum)
{
    int s=0;char c=getchar();
    while (c<'0' || c>'9') c=getchar();
    while (c>='0' && c<='9') s=s*10+c-48,c=getchar();
    readnum=s;return;
}

inline bool check_in(int x,int y){
	return x>=0 && y>=0;
}

int main()
{
	freopen("meteor.in","r",stdin);
	freopen("meteor.out","w",stdout);
	int m;
	read(m);
	for (int i=1;i<=m;++i)
	{
		read(star[i].x);
		read(star[i].y);
		read(star[i].t);
	}
	memset(danger,127,sizeof(danger));
	for (int i=1;i<=m;++i) 
	{
		int px=star[i].x,py=star[i].y;
		danger[px][py]=min(star[i].t,danger[px][py]);
		for (int j=0;j<4;++j)
		{
			int nx=px+dx[j],ny=py+dy[j];
			if (check_in(nx,ny)) danger[nx][ny]=min(danger[nx][ny],star[i].t);
		}
	}//标记所有会出现危险的节点所出现危险的时间 
	memset(d,127,sizeof(d));
	int h=1,t=1;d[0][0]=0;v[0][0]=1;qx[1]=qy[1]=0;
	for (;h<=t;++h)
	{
		int px=qx[h],py=qy[h];
		if (d[px][py]<danger[px][py] && danger[px][py]>=10000000) 
		{
			cout<<d[px][py]<<endl;
			flag=0;
			break;
		}//到达安全地带 
		for (int i=0;i<4;++i)
		{
			int nx=px+dx[i],ny=py+dy[i];
			if (!check_in(nx,ny) || v[nx][ny]) continue;
			if (d[px][py]+1<danger[nx][ny])
			{
				d[nx][ny]=d[px][py]+1;
				v[nx][ny]=1;
				qx[++t]=nx,qy[t]=ny;
			}
		}
	}
	if (flag) cout<<"-1\n";
	return 0;
}

安全回家

Zhouyibo_2023
登出

UOJ Logo 海亮教育信奥训练平台(校内版)
首页

题库
作业
比赛
积分
解题数
提交记录
Hack!
博客
帮助

好评差评[-7]
普及组noip2018赛前练习
B. 2安全回家
6 d 21:59:13
时间限制:1s 空间限制:256MB 输入文件:vuk.in 输出文件:vuk.out
当前24小时内您还剩19次提交本题的机会

描述
提交
解题讨论区
自定义测试
返回

题目描述
Farmer John在森林里迷路了,现在他急需要回家。
森林被分成了N*M的矩阵,每个单元格有一个字符来标示属性,’.‘表示空地,‘V’表示Fj所在的位置,‘J’表示Fj家的位置。在森林里有很多危险的地方,危险的地方被标记为’+’。 Fj想要在回家的路上,让自己距离危险的地方尽可能的远,请你帮他设计一条线路,让这条线路上的每个点距离 危险的地方 最近的距离 最大。
假设Fj的位置为 (R,C),某个危险点的位置为 (A,B)Fj距离危险点的距离为:
|R-A| + |C-B|
当然,Fj必须要回家,也许他必须要经过危险的地方。
输入格式
第一行两个整数:N和M (1 ≤ N, M ≤ 500)
接下来N行,每行M个字符,范围是:’.’, ‘+’, ‘V’, ‘J’.
数据保证肯定有’V’, ‘J’,且至少有一个’+’
输出格式
一个整数,表示那个距离
样例数据
input

4 4
+…


V…J
output
3
input
4 5

.+++.
.+.+.
V+.J+
output
0

题解
对于这道题,我们可以选择做两遍搜索(dbfs)。
第一遍:
广搜bfs。预处理出每一个位置距离陷进的最短距离。将多个陷进节点在广搜前加入队列,距离设置为0即可得到一个数字dis[i][j]表示位置i,j距离陷进的最短距离。
第二遍:
深搜dfs。因为所求的是回家的线路中距离陷进的距离的最大值,因此我们只要在dis数组上面遍历每一条路径并求出这个路径的最小值,使得最终的这个最小值最大即可;为了优化深搜,这里加上记忆化搜索,f[i][j]表示任何经过i,j的路径的最小值的最大值。显然,最后输出记忆画数组的结果即可。
CODE

#include<bits/stdc++.h>
using namespace std;
#define maxn 600

struct Node{
	int x,y;
};
Node st,ed;
int n,m,h=1,t=0,ans=0;
char a[maxn][maxn];
int d[maxn][maxn];
bool v[maxn][maxn];
Node q[maxn*maxn];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int f[maxn][maxn];//存储所有最小值中的最大值 

inline void read(int &readnum)
{
    int s=0;char c=getchar();
    while (c<'0' || c>'9') c=getchar();
    while (c>='0' && c<='9') s=s*10+c-48,c=getchar();
    readnum=s;return;
}

inline int check_out(int x,int y){
	return x<1 || x>n || y<1 || y>m;
}

void readnum()
{
	read(n);
	read(m);
	for (int i=1;i<=n;++i)
	    for (int j=1;j<=m;++j)
	    {
	    	cin>>a[i][j];
	    	if (a[i][j]==43) 
			{
				v[i][j]=1;
				d[i][j]=0;
				q[++t]=(Node){i,j};
			} 
			if (a[i][j]==86) st=(Node){i,j};
			if (a[i][j]==74) ed=(Node){i,j};
	    }
}

void dfs(int x,int y,int Min)
{
	if (Min<=f[x][y]) return;
	f[x][y]=Min;
	if (x==ed.x && y==ed.y) return;
	for (int i=0;i<4;++i)
	{
		int nx=x+dx[i],ny=y+dy[i];
		if (check_out(nx,ny)) continue;
		dfs(nx,ny,min(Min,d[nx][ny]));
	} 
	return;
}

void bfs()
{
	for (;h<=t;++h)
	{
		Node p=q[h];
		for (int i=0;i<4;++i)
		{
			int nx=p.x+dx[i],ny=p.y+dy[i];
			if (check_out(nx,ny) || v[nx][ny]) continue;
			q[++t]=(Node){nx,ny};
			v[nx][ny]=1,d[nx][ny]=d[p.x][p.y]+1;
		}
	}
}

int main()
{
	freopen("vuk.in","r",stdin);
	freopen("vuk.out","w",stdout);
	memset(f,-1,sizeof(f));
	readnum();
	bfs();
	dfs(st.x,st.y,d[st.x][st.y]);
	printf("%d",f[ed.x][ed.y]);
	return 0;
}

黑洞

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

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

这道题我们可以选择深搜dfs对所有的黑洞亮亮配对,然后判环:
我们需要预处理每一个节点相同纵坐标,横坐标距离它最近且在正方向时的接点编号。
设上述结果存储在next[]里面,与第i个节点配对点存储在with[],则只有若干个with[next[i]]出现重复则表示出现了环。

CODE

#include<bits/stdc++.h>
using namespace std;
#define maxn 15

struct Node{
	int x,y;
}a[maxn];
int n,ans=0;
int next[maxn],with[maxn];

inline void read(int &readnum)
{
    int s=0;char c=getchar();
    while (c<'0' || c>'9') c=getchar();
    while (c>='0' && c<='9') s=s*10+c-48,c=getchar();
    readnum=s;return;
}

inline bool cmp(Node u,Node v)
{
	if (u.y==v.y) return u.x<v.x;
	else return u.y<v.y;
}
//y坐标为排序的第一基准,x坐标为排序的第二基准 

inline bool check(int x)
{
	//判断x是否存在环 
	int v[maxn]={};
	while (next[x])//如果next[x]=0表示无法向右走 
	{
		if (v[x]) return true;//遍历到自己了 
		v[x]=1;//标记遍历到的节点 
		x=with[next[x]];//直接进行穿越(只有通过穿越的情况再能再次遇见自己,所以不用到他的右边) 
	}
    return false;
}

void dfs(int x)
//表示前x对已经完全配对完了 
{
	if (x==n)
	{
		for (int i=1;i<=n;++i)
			if (check(i))
			{
				ans++;
				return;
			}
		//枚举每一个节点是否存在环 
		return;
	}
	int part1=x+1;
	if (with[part1]) 
	{
	    dfs(x+1);
	    return;
	}
	//如果第x+1对已经被配对就继续往下配对					
	for (int i=part1+1;i<=n;++i)
	{
		if (with[i]) continue;
		//如果已经被前面的点配对则退出 
		int part2=i;
		with[part1]=part2;
		with[part2]=part1;
		dfs(x+1);
		with[part1]=with[part2]=0;
	} 
}

int main(void)
{
	freopen("aaaaa.in","r",stdin);
	freopen("aaaaa.out","w",stdout);
	read(n);
	for (int i=1;i<=n;++i) read(a[i].x),read(a[i].y);
	sort(a+1,a+n+1,cmp);
	for (int i=1;i<n;++i)
	    if (a[i].y==a[i+1].y) next[i]=i+1;
	dfs(0);
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Ronaldo7_ZYB/article/details/83653804