搜索回溯训练

搜索回溯训练

一、题目列表
1. Red and Black (https://vjudge.net/problem/POJ-1979
2. The Sultan’s Successors (https://vjudge.net/problem/UVA-167
3. The Settlers of Catan(https://vjudge.net/problem/POJ-2258
4. Transportation(https://vjudge.net/problem/POJ-2258
5. Don’t Get Rooked(https://vjudge.net/problem/POJ-1315
6. 8 Queens Chess Problem(https://vjudge.net/problem/UVA-750
二、题目分析**
----------Red and Black
题意:求从起点即@的位置出发能走到所有黑色砖块。
思路:从起点直接暴力搜索,使用方向数组往4个方向搜,能到的地方全都用@标记。

#include<cstdio>
#include<iomanip>
#include<string>
#include<iostream>
#include<queue>
using namespace std;
int dx[]={0,0,1,-1};//4个方向的方向数组
int dy[]={1,-1,0,0};
char g[35][35];
int n,a,b,m; 
void dfs(int x,int y)
{
	for(int i=0;i<4;i++)
	{
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(0<=xx&&xx<m&&yy>=0&&yy<n&&g[xx][yy]!='#'&&g[xx][yy]!='@')
		{
			g[xx][yy]='@';
			dfs(xx,yy);
		}
	}
}
int main()
{
	int x,y,sum=0;
	while(cin >> n >> m&&n&&m){
		sum=0;
		for(int i=0;i<m;i++)
			for(int j=0;j<n;j++) 
			{
				cin >> g[i][j];
				if(g[i][j]=='@')//记录起点的坐标
					x=i,y=j;
			}
		dfs(x,y);
		for(int i=0;i<m;i++)
			for(int j=0;j<n;j++)
				if(g[i][j]=='@')
						sum++;
		cout << sum <<endl;
	}
    return 0;
}

----------The Sultan’s Successors
题意:给出8*8棋盘各点分数,求按规则摆放的8个皇后棋子对应各点分数和的最大值。
思路:搜索出所有摆棋的可能,每次搜完更新一下最大值。(八皇后的规则是每行每列且每条斜率为±1的直线中只允许有一个棋子),按一行一个或一列一个进行向后搜索,故需要3个标记数组控制棋子的摆放。

#include<cstdio>
#include<iomanip>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
int vis[15]={0},l[30]={0},r[30]={0};//斜率分别为+-1的不同直线
int s[10]={0};//用来存下每行棋子的坐标
int g[10][10];
int n,a,b,m,t,max1=-1; 
void dfs(int x)
{
	if(x==9)
	{
		int sum=0;
		for(int i=1;i<=8;i++)
			sum+=g[s[i]][i];
		max1=max(max1,sum);
	}
	for(int i=1;i<=8;i++)
	{
		{
			if(!vis[i]&&!r[x-i+8]&&!l[x+i])//因为x-i小于0且最小值为-7,所以+8向上平移8.
			{
				vis[i]=1;r[x-i+8]=1;l[x+i]=1;
				s[x]=i;
				dfs(x+1);
				vis[i]=0;r[x-i+8]=0;l[x+i]=0;
			}
		}
	}
}
int main()
{
	cin >> n;
	while(n--){
		max1=-1;
		memset(vis,0,sizeof(vis));
		memset(l,0,sizeof(l));
		memset(r,0,sizeof(r));
		for(int i=1;i<=8;i++)
			for(int j=1;j<=8;j++)
				cin >> g[i][j];
		dfs(1);
		printf("%5d\n",max1);
	}
    return 0;
}

----------The Settlers of Catan
题意:给出一个无向图,求最长路径的长度,点可重复使用,边只可以使用一次。
思路:搜索各个点所能走到的最长路,更新最大值,可以选择用邻接矩阵存图。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
int n,m,max1=-1;
vector<int> g[50];
int vis[30][30]={0};
void dfs(int n,int l)
{
	max1=max(max1,l);
	for(int i=0;i<g[n].size();i++)
	{
		if(!vis[n][g[n][i]]&&!vis[g[n][i]][n])//无向图
		{
			vis[g[n][i]][n]=vis[n][g[n][i]]=1;
			dfs(g[n][i],l+1);
			vis[g[n][i]][n]=vis[n][g[n][i]]=0;
		}
	}
}
int main()
{
	while(cin >> n >>m&&n&&m)
	{
		memset(vis,0,sizeof(vis));
		max1=-1;
		int x,y;
		for(int i=1;i<=m;i++)
		{
			cin >> x >> y;
			g[x].push_back(y);
			g[y].push_back(x);
		}
		for(int i=0;i<n;i++)
		{
			dfs(i,0);
		}
		cout << max1 <<endl;
		for(int i=0;i<n;i++)
			g[i].clear();//记得清除数据。
	}
    return 0;
}

----------Transportation
题意:给出列车的最大乘客量,站数,订单数和不同起始车站的间的票数,求整趟列车的最大收益(考虑的列车的最大乘客量,到达每个车站只能全部接受或全部拒绝该站的订单,即车票数加上列车现有乘客数小于最大容纳量才会接受订单)。
思路:使用结构体存下所有订单的数据,注意!!!因为存在上下车问题,需要对所有订单按照,出发站从小到大排序,同时不同于一般的题型,该题直接按订单搜索(想了好久按站点搜索)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
int nn,m,z,max1;
int le[30];//保留各个车站下车人数。
struct node{
	int now;
	int to;
	int num;
}s[30];
bool cmp(node x,node y)//重写cmp函数
{
	return x.now<y.now;
}
void dfs(int n,int earn,int sum)//sum是车上的人数 
{
	if(n==z)
	{
		max1=max(max1,earn);
		return ;
	}
	if(n>0)
		for(int i=s[n-1].now+1;i<=s[n].now;i++)//从上一个订单到需要处理的这个订单间 处理各站下车人数 。 
			sum-=le[i];//下车
	if(nn>=s[n].num+sum)//判断能否接下这个订单
	{
		le[s[n].to]+=s[n].num;
		dfs(n+1,earn+s[n].num*(s[n].to-s[n].now),sum+s[n].num);
		le[s[n].to]-=s[n].num;
	}
	dfs(n+1,earn,sum);
	
}
int main()
{
	while(cin >> nn >>m >> z&&nn&&m)
	{
		memset(le,0,sizeof(le));
		max1=-1;
		int x,y,num;
		for(int i=0;i<z;i++)
		{
			cin >> x >> y >>num;
			s[i].now=x,s[i].to=y,s[i].num=num;
		}
		sort(s,s+z,cmp);
		dfs(0,0,0);
		cout << max1 <<endl;
	}
    return 0;
}

----------Don’t Get Rooked
题意:求4*4且有障碍棋盘中能摆放多少个车,使之两两不互吃(不在同一行列或在同一行列中且其中存在障碍则不会互吃)。
思路:以每个非障碍点为起点,开始搜索棋子摆放棋盘最多能摆多少个棋子,每摆一次则标记一次,搜索完则取消标记。需要写一个函数来判断摆放是否合法。

#include<cstdio>
#include<iomanip>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
char g[10][10];
int n,a,b,m,t,max1=-1; 
bool check(int x,int y)//判断摆放是否合法,要么先碰到边界,要么先碰到障碍。
{
	for(int i=1;x+i<n&&g[x+i][y]!='X';i++)
		if(g[x+i][y]=='1')
			return 0;
	for(int i=1;x-i>=0&&g[x-i][y]!='X';i++)
		if(g[x-i][y]=='1')
			return 0;
	for(int i=1;y+i<n&&g[x][y+i]!='X';i++)
		if(g[x][y+i]=='1')
			return 0;
	for(int i=1;y-i>=0&&g[x][y-i]!='X';i++)
		if(g[x][y-i]=='1')
			return 0;
	return 1;
}
void dfs(int num)
{
	max1=max(max1,num);
	for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
			{
				if(g[i][j]=='.'&&check(i,j))
				{
					g[i][j]='1';
					dfs(num+1);
					g[i][j]='.';//简单回溯
				}
			}
}
int main()
{
	while(cin >> n&&n){
		max1=-1;
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				cin >> g[i][j];
	dfs(0);
	cout << max1 <<endl;
	}
    return 0;
}

---------- 8 Queens Chess Problem
题意:经典八皇后问题,固定一个棋子的位置,求出所有棋子摆放的可能,按字典序打印。
思路:从一开始标记好固定的点,按列增加,每列8个行从小到大进行搜索。

#include<cstdio>
#include<iomanip>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
int vis[15]={0},l[30]={0},r[30]={0};
int s[10]={0};//存点
int g;
int n,a,b,m,t; 
void dfs(int x)
{
	if(x==9)
	{
		printf("%2d     ",t++);
		for(int i=1;i<=8;i++)
			cout <<' '<< s[i];
		cout << endl;
	}
	if(x == b) { dfs(x + 1); return; }//遇到固定点直接跳过,往下搜索.
	for(int i=1;i<=8;i++)
	{
		{
			if(!vis[i]&&!r[x-i+8]&&!l[x+i])
			{
				vis[i]=1;r[x-i+8]=1;l[x+i]=1;
				s[x]=i;
				dfs(x+1);
				vis[i]=0;r[x-i+8]=0;l[x+i]=0;
			}
		}
	}
}
int main()
{
	cin >> n;
	while(n--){
		t=1;
		cin >> a >> b;
		memset(vis,0,sizeof(vis));
		memset(l,0,sizeof(l));
		memset(r,0,sizeof(r));
		vis[a]=1,l[a+b]=1;r[b-a+8]=1;
		s[b]=a;
		printf("SOLN       COLUMN\n #      1 2 3 4 5 6 7 8\n\n");
		dfs(1);
		if(n) 
			printf("\n");
	}
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值