王道机试指南--第六章(搜索)

BFS

题目6.3链接非常可乐
这道题是隐含的BFS题目,重点在于状态的构建和状态如何转化。状态就是定义的结构体,转化就是BFS遍历。具体思路参考书目。

代码如下:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;

const int MAX = 101;
int visit[MAX][MAX][MAX];
int S, N, M, t[10];
struct Node
{
    int a, b, c, t;
    Node(int a, int b, int c, int t):a(a), b(b), c(c), t(t){}
};
queue<Node> q;
void AtoB(int &a, int &b, int sb)//A中的倒进B中
{
    if(a>sb-b)
    {
        a = a-(sb-b);
        b = sb;
    }
    else
    {
        b += a;
        a = 0;
    }
}
void bfs()
{
    memset(visit, 0, sizeof(visit));
    while(!q.empty()) q.pop();
    q.push(Node(S, 0, 0, 0));
    visit[S][0][0] = 1;
    while(!q.empty())
    {
        Node u = q.front(); q.pop();
        //cout << u.a << " " << u.b << " " << u.c << " " << u.t << endl;
        if((u.b==S/2 && u.c==S/2 && u.a==0) || (u.a==S/2 && u.b==S/2 && u.c==0) || (u.a==S/2 && u.c==S/2 && u.b==0))//要保证绝对分半
        {
            cout << u.t << endl;
            return;
        }
        t[0] = u.a, t[1] = u.b, t[2] = u.c;
        t[3] = S, t[4] = N, t[5] = M;//记录每个杯子的状态和其最大容量,方便一下遍历
        for(int i=0; i<3; i++)
        {
            for(int j=0; j<3; j++)
            {
                if(i==j) continue;
                if(t[i]==0) continue; //t[i]为空
                if(t[j]==t[j+3]) continue;//t[j]已满
                int ta = t[i], tb = t[j];
                AtoB(t[i], t[j], t[j+3]);
                if(visit[t[0]][t[1]][t[2]]==0)
                {
                    visit[t[0]][t[1]][t[2]] = 1;
                    q.push(Node(t[0], t[1], t[2], u.t+1));
                }
                t[i] = ta, t[j] = tb;
            }
        }
    }
    cout << "NO" << endl;
    return;
}
int main()
{
    while(scanf("%d%d%d", &S, &N, &M)!=EOF && S)
    {
        bfs();
    }
    return 0;
}
递归和回溯
素数环

题目链接:素数环
题目大意:求出给定数的素数环。
思路:递归的思路。设置visit数组来标记是否已经放问过,ans数组来存储结果,选取未访问的结点,并判断新的邻居之和是否为素数,是则存储,不是则重新选取。
代码如下:

#include <iostream>
#include <cstring>
using namespace std;

int ans[22];
int visit[22];
int n;

bool isPrime(int n)
{
    for(int i=2; i*i<=n; i++)
    {
        if(n%i==0)
            return 0;
    }
    return 1;
}
void Print(int n)
{
    for(int i=1; i<=n; i++)
    {
        if(i!=1) cout << " ";
        cout << ans[i];
    }
    cout << endl;
}
void Ring(int num)
{
    if(num>1)
    {
        if(isPrime(ans[num]+ans[num-1])==0)
            return;
    }
    if(num==n)
    {
        if(isPrime(ans[num]+ans[1])==0)
            return;
        Print(num);
    }
    for(int i=2; i<=n; i++)
    {
        if(visit[i]==0)
        {
            visit[i] = 1;
            ans[num+1] = i;
            Ring(num+1);
            visit[i] = 0;
        }
    }
}
int main()
{
    int T = 1;
    while(cin >> n)
    {
        memset(visit, 0, sizeof(visit));
        visit[1] = 1;
        ans[1] = 1;
        cout << "Case " << T++ << ":" << endl;
        Ring(1);
        cout << endl;
    }
    return 0;
}

方法二:这个方法简单易理解
代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX 22
#define repf(i,from ,to) for(int i =from ; i <to ; i++)
using namespace std;
 
 
int visit[MAX];
int n;
int num[MAX];
 
bool isPrime(int a )
{
  if(a==1)
    return false ;
  for(int i = 2;i<=sqrt(a);i++)
  {
      if(a%i==0)
      {
          return false ;
      }
  }
  return true ;
}
//迭代深搜
void dfs(int step)
{
   if(step>n&&isPrime(num[n]+num[1]))
   {
       for(int i = 1 ; i <n ; i ++)
        cout << num[i]<<" ";
        cout << num[n]<<endl;
   }
   for(int i = 2 ; i <=n ; i ++)
   {
       num[step]=i;
       if(isPrime(num[step]+num[step-1])&&!visit[i])
       {
           visit[i]=1;
           dfs(step+1);
           visit[i]=0;
       }
   }
}
int main(){
 int c = 1 ;
  while(  cin >> n )
   {
    cout<<"Case "<<c++<<":"<<endl;
    memset(num,0,sizeof(num));
    num[1] = 1;
    dfs(2);
    cout <<endl;
   }
   return 0 ;
 
}
全排列

题目链接:全排列
题目大意:输出给定字符序列的全排列。
思路:回溯遍历。
代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int MAX = 10;
char s[MAX], q[MAX];
int visit[MAX], m;

void DFS(char s[], int t){
    q[m++] = s[t];
    if(m==strlen(s)){
        printf("%s\n", q);
        return;
    }
    for(int j=0; j<strlen(s); j++){
        if(visit[j]==0){
            visit[j] = 1;
            DFS(s, j);
            m--;
            visit[j] = 0;
        }
    }
}
int main(){
    while(scanf("%s", s)!=EOF){
        int len = strlen(s);
        sort(s, s+len);

        for(int i=0; i<len; i++){
            m = 0;
            memset(visit, 0, sizeof(visit));
            visit[i] = 1;
            DFS(s, i);
        }
        cout << endl;
    }
    return 0;
}

带减枝的DFS

题目链接:Tempter of the Bone
参考博客:HDU 1010 && ZOJ 2110–Tempter of the Bone【DFS && 奇偶剪枝】
题目大意:在规定的步数时要到达目的地。
思路:需要在DFS的过程中加入减枝(包括奇偶减枝:超出最短路径的部分一定是偶数)。
代码:

#include <iostream>
#include <cmath>
using namespace std;
int N, M, T, sx, sy, ex, ey;
char map[6][6];
const int dir[4][2] = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
bool solved =  false, arrd[6][6];
int Distance ( int x, int y ) 
{
	return abs ( (double)x - ex ) + abs ( (double)y - ey ); // 当前点(x,y)到终点(ex,ey)的最短距离
}
void DFS ( int x, int y, int step )
{
	if ( solved ) return;
	if ( map[x][y] == 'D' && step == T ) {
		solved = true;
		return;
	}
	if ( step >= T ) return;                    // 当前时间即步数(step) >= T 而且还没有找到D点
	int dis = T - step - Distance ( x, y );
	if ( dis < 0 || dis % 2 ) return;           // 剩余步数小于最短距离或者满足奇偶剪枝条件
	for ( int i = 0; i < 4; i += 1 ) {
		int tx = x + dir[i][0];
		int ty = y + dir[i][1];
		int tstep = step + 1;
		if ( tx >= 0 && tx < N && ty >= 0 && ty < M && map[tx][ty] != 'X' && !arrd[tx][ty]) {
			arrd[tx][ty] = true;
			DFS ( tx, ty, tstep );
			arrd[tx][ty] = false;
		}
	}
}
int main ( int argc, char *argv[] )
{
	while ( cin >> N >> M >> T, N+M+T ) {
		solved = false;
		int xnum = 0;                           // 记录'X'的数量
		for ( int i = 0; i < N; i += 1 ) {
			cin.get();
			for ( int j = 0; j < M; j += 1 ) {
				cin >> map[i][j];
				arrd[i][j] = false;
				if ( map[i][j] == 'S' ) {
					sx = i;
					sy = j;
					arrd[i][j] = true;
				}
				else if ( map[i][j] == 'D' ) {
					ex = i;
					ey = j;
				}
				else if ( map[i][j] == 'X' ) {
					xnum++;
				}
			}
		}
		if ( N * M - xnum > T ) { // 可通行的点必须大于要求的步数,路径剪枝。
			DFS ( sx, sy, 0 );
		}
		if ( solved )
			cout << "YES" << endl;
		else 
			cout << "NO" << endl;
	}
	return 0; 
}

书上的代码:

#include <cstdio>
using namespace std;

char maze[8][8];
int n, m, t;
int sucess;
int go[][2] = {1, 0, -1, 0, 0, 1, 0, -1};
void DFS(int x, int y, int time){
    if(sucess) return;
    for(int i=0; i<4; i++){
        int nx = x + go[i][0];
        int ny = y + go[i][1];

        if(nx<=0 || nx>n || ny<=0 || ny>m) continue;
        if(maze[nx][ny]=='X') continue;
        if(maze[nx][ny]=='D'){
           if(time+1==t){
                sucess = 1;
                return;
           }else{
                continue;
           }
        }
        maze[nx][ny] = 'X';
        DFS(nx, ny, time+1);
        maze[nx][ny] = '.';
    }
}

int main(){
    while(scanf("%d%d%d", &n, &m, &t) && n){
        for(int i=1; i<=n; i++){
            scanf("%s", maze[i]+1);
        }
        sucess = 0;
        int sx, sy;
        for(int i=1; i<=n; i++){
            for(int j=1; j<=m; j++){
                if(maze[i][j]=='D'){
                    sx = i, sy = j;
                }
            }
        }

        for(int i=1; i<=n; i++){
            for(int j=1; j<=m; j++){
                if(maze[i][j]=='S' && (i+j)%2==((sx+sy)%2+t)%2){
                    maze[i][j] = 'X';
                    DFS(i, j, 0);
                }
            }
        }
        if(sucess) printf("YES\n");
        else       printf("NO\n");
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值