搜索回溯训练
一、题目列表
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;
}