搜索与搜索剪枝2

广搜

给定一个迷宫,求(0,0)到终点(6,5)的最短距离。

细节问题:可以通过将(x,y)处理为x*tmp+y的形式进行降维打击,这样更好向queue里存数据;另外,计算步数当然是根据前一步的步数算啦!QAQ

/*
6 5
1 1 0 1 1
1 0 1 1 1
1 0 1 0 0
1 0 1 1 1
1 1 1 0 1
1 1 1 1 1
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=405,inf=0x3f3f3f3f;
int n,m,dis[maxn][maxn],mp[maxn][maxn],tmp=100;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
queue<int>q;
void bfs(){
	memset(dis,-1,sizeof(dis));
	dis[1][1]=0;
	int pos=tmp*1+1;
	int cnt=0;
	q.push(pos);
	while(!q.empty()){
		pos=q.front(); q.pop(); cnt++;
		int nx=pos/tmp, ny=pos%tmp;
		for(int i=0;i<4;++i){
			int cx=nx+dx[i], cy=ny+dy[i];
			if(cx<1||cy<1||cx>n||cy>m||dis[cx][cy]!=-1||!mp[cx][cy])
				continue;
			dis[cx][cy]=dis[nx][ny]+1;//
			q.push(cx*tmp+cy);
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			cin>>mp[i][j];
	bfs();
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
			printf("%3d",dis[i][j]);
		cout<<endl;
	}
	return 0;
}

马的遍历 - 洛谷

有一个 n×m 的棋盘,在某个点(x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步

细节问题:降维打击时,一定要用比数据范围略大的数进行!比如如果数最大为15,用x*5+y可能会出现x为4、y为5以及x为3、y为10的情况……QAQ

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=405,inf=0x3f3f3f3f;
int n,m,x,y,dis[maxn][maxn],mp[maxn][maxn],tmp=500;
int dx[9]={1,1,-1,-1,2,2,-2,-2};
int dy[9]={2,-2,2,-2,1,-1,1,-1};
queue<int>q;
void bfs(){
	memset(dis,-1,sizeof(dis));
	dis[x][y]=0;
	q.push(x*tmp+y);
	while(!q.empty()){
		int pos=q.front(); q.pop();
		int nx=pos/tmp, ny=pos%tmp;
		for(int i=0;i<8;++i){
			int cx=nx+dx[i], cy=ny+dy[i];
			if(cx<1||cy<1||cx>n||cy>m||dis[cx][cy]!=-1)
				continue;
			dis[cx][cy]=dis[nx][ny]+1;
			q.push(cx*tmp+cy);
		}
	}
}
int main(){
	cin>>n>>m>>x>>y;
	bfs();
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j)
			printf("%-5d",dis[i][j]);
		cout<<endl;
	}
	return 0;
}

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网


数独挑战

玩家需要根据 9×99 \times 99×9 盘面上的已知数字,推理出所有剩余位置的数字,并满足每一行、每一列、每一个粗线九宫格内的数字包含有 1-9 的数字,且不重复。

现在给你一个数独,请你解答出来。每个数独保证有且只有一个解。

细节问题:时间优化方面,可以先将未填充的格子进行记录,从而减少在深搜上花费的时间;由于小方格的位置确定比较麻烦,不防对其进行打表,这样只要将行列坐标输入即可查询所属的小方格编号。

另外,当数据量较大时,可以考虑先解决已经快确定的答案(就是那种在行或列或小方格之中几乎只需要填它一个的那种数),但sort也会导致其时间复杂度上升。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=405,inf=0x3f3f3f3f;
int h[15][15],l[15][15],cub[15][15],mp[15][15],cnt;
//int cnth[15],cntl[15],cntc[15];
struct ty{
	int x,y;
	int num;
}bk[90];//记录要填数字的格子,减少深搜时间 
const int xiao[15][15]={{0,0,0,0,0,0,0,0,0,0},
						{0,1,1,1,2,2,2,3,3,3},
						{0,1,1,1,2,2,2,3,3,3},
						{0,1,1,1,2,2,2,3,3,3},
						{0,4,4,4,5,5,5,6,6,6},
						{0,4,4,4,5,5,5,6,6,6},
						{0,4,4,4,5,5,5,6,6,6},
						{0,7,7,7,8,8,8,9,9,9},
						{0,7,7,7,8,8,8,9,9,9},
						{0,7,7,7,8,8,8,9,9,9}};//打表,对相对应的小方格进行定位 
//bool cmp(ty a,ty b){
//	return a.num>b.num;
//}
void print(){
	for(int i=1;i<=9;++i){
		for(int j=1;j<=9;++j){
			printf("%d ",mp[i][j]);
		}
		cout<<endl;
	}
}
void dfs(int dep){
	if(dep>cnt){
		print();
		return ;
	}
	for(int i=1;i<=9;++i){
		int x=bk[dep].x;
		int y=bk[dep].y;
		if(h[x][i]||l[y][i]||cub[xiao[x][y]][i]) continue;
		mp[x][y]=i; h[x][i]=1; l[y][i]=1; cub[xiao[x][y]][i]=1;
		dfs(dep+1);
		h[x][i]=0; l[y][i]=0; cub[xiao[x][y]][i]=0;
	}
}
int main(){
	for(int i=1;i<=9;++i)
	for(int j=1;j<=9;++j){
		scanf("%d",&mp[i][j]);
		if(mp[i][j]){
			h[i][mp[i][j]]=1;//第i行有数字
			l[j][mp[i][j]]=1;
			cub[xiao[i][j]][mp[i][j]]=1;//第方格有数字 
		}else{
			bk[++cnt].x=i; bk[cnt].y=j;
//			cnth[i]++; cntl[j]++;
//			cntc[xiao[i][j]]++;
		}
	}
//	for(int i=1;i<=cnt;++i)
//		bk[i].num=max(cnth[bk[i].x],max(cntl[bk[i].y],cntc[xiao[bk[i].x][bk[i].y]]));
//	sort(bk+1,bk+1+cnt,cmp);
	dfs(1);
	return 0;
}

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

幸运数字

定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如说,47、744、4都是幸运数字而5、17、467都不是。
定义next(x)为大于等于x的第一个幸运数字。给定l,r,请求出next(l) + next(l + 1) + ... + next(r - 1) + next(r)。

第一次意识到,造有规律的数据可以用广搜!

细节问题:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1030,inf=0x3f3f3f3f;
ll l,r,ans,sav[maxn];
queue<ll>q;
void mk(){
	ll sum=0,cnt=0,nw=0;
	q.push(4); q.push(7);
	while(nw<r){
		nw=q.front(); q.pop();
		sav[++cnt]=nw;
		sum=nw*10+4; q.push(sum);
		sum=nw*10+7; q.push(sum);
	}
}
int main(){
	cin>>l>>r;
	mk();
	int p=0;
	for(int i=1;l<=r;i++){
		while(p+l<=r&&sav[i]>=p+l) p++;
		ans+=sav[i]*p;
		l+=p; p=0;
		if(l>r){
			cout<<ans;
			return 0;
		}
	}
}

链接:https://ac.nowcoder.com/acm/problem/16417
来源:牛客网

[NOIP2017]奶酪


现有一块大奶酪,它的高度为 h,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系, 在坐标系中,奶酪的下表面为 z = 0,奶酪的上表面为 z = h。
现在, 奶酪的下表面有一只小老鼠 Jerry, 如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交, Jerry 则可以从奶酪下表面跑进空洞; 如果一个空洞与上表面相切或是相交, Jerry 则可以从空洞跑到奶酪上表面。位于奶酪下表面的 Jerry 想知道, 在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去? 

我的想法是,边读入数据边计算任意两点之间的距离,当两个空洞相交或相切时,同时用vis数组标记两点之间是否可通,同时记录两点之中距离最高点最近的位置的坐标和最矮坐标,从最矮坐标<=0的位置进行深搜;深搜的同时标记已经走过的点。自己的想法没错,但TLE了很久,后来对比答案居然发现了新大陆!所以说耐心调自己造的bug是有好处的QAQ

回溯之后将vis还原至0是深搜中常用的操作。但为啥呢?分析了在走迷宫中它的用处以后发现,如果没有还原也不是什么致命的错误,反而还省了几行代码:

 上图中若原始输入数据为0则说明迷宫有障碍,并没有在回溯后进行vis=0。但不管怎样人家还是可以遍历的呀!我们想一下什么时候非vis=0不可.想想排列问题,如果一个数字我们标记了,就说明我们不能以后再使用了,这个时候求完一个答案就必须vis=0,要不数以后就没法用了!但在走迷宫时,我们的目的是遍历整个迷宫,不管这个点有没有标记,我们都可以不断去探索其他点,vis=1相当于告诉我们不要重蹈覆辙(因为既然你还在走,而又碰到了一个以前走过的点,说明这路你之前走过却还没到目的地,所以肯定不走这条老路!),而这并不耽误你回溯到之前有其他路可选的时刻再去探索。这道奶酪题就是在走老路时给我增加了时间导致我TLE。所以说不能说到深搜就在回溯后vis=0,vis=1是在提醒你不要重蹈覆辙。

细节问题:对于开根号的问题(求距离)为了防止精度丢失就直接用它的平方判断吧!另外一定别忘了判断数据范围大数开longlong!另外我们注意到,要想在找到答案的一刻立刻退出深搜,可以通过使深搜返回int值+回溯判断的方式达成目的~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1030,inf=0x3f3f3f3f;
struct ty{
	ll x,y,z;
}p[maxn];
ll t,n,h,r,f;
int hig[maxn],low[maxn],vis[maxn],acs[maxn][maxn];
int dis(int x,int y){
		return ((p[x].x - p[y].x) * (p[x].x - p[y].x)
	        + (p[x].y - p[y].y) * (p[x].y - p[y].y)
	        + (p[x].z - p[y].z) * (p[x].z - p[y].z) <= (4 * r * r));
}//
int dfs(int x){
	if(hig[x]>=h){
		f=1;
		printf("Yes\n");
		return 1;
	}
	for(int i=1;i<=n;++i){
		if(vis[i]==0&&acs[x][i]){
			vis[i]=1;
            if(dfs(i)) return 1;
//            vis[i]=0;
		}
	}
    return 0;
}
int main(){
	scanf("%lld",&t);
	while(t--){
		memset(acs,0,sizeof(acs));
		memset(hig,0,sizeof(hig));
		memset(vis,0,sizeof(vis));
		scanf("%lld%lld%lld",&n,&h,&r);
		f=0;
		for(int i=1;i<=n;++i) low[i]=inf;
		for(int i=1;i<=n;++i){
			scanf("%lld%lld%lld",&p[i].x,&p[i].y,&p[i].z);
			low[i]=p[i].z-r;
			hig[i]=p[i].z+r;
			for(int j=1;j<i;++j){
				if(dis(i,j))
					acs[i][j]=acs[j][i]=1;
			}
		}
		for(int i=1;i<=n;++i){
			if(low[i]<=0) {
				vis[i]=1; 
                dfs(i);
                if(f) break;
//                vis[i]=0;
			}
		}
		if(!f) printf("No\n");
	}
	return 0;
}

乱入:如何求两圆相交阴影部分的面积?

下图中可用海伦公式求出绿色三角形的面积,从而得出相交两点和两圆心构成的四边形面积、三角形底角大小,求出两个扇形面积后相加减去四边形的面积即可。

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

wyh的迷宫

给你一个n*m的迷宫,这个迷宫中有以下几个标识:

s代表起点;t代表终点;x代表障碍物;.代表空地

现在你们涵哥想知道能不能从起点走到终点不碰到障碍物(只能上下左右进行移动,并且不能移动到已经移动过的点)。

居然忘了有障碍这回事……我也是无语……

越界、障碍、是否重复,3个判断缺一不可!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值