kuangbin专题一 简单搜索

基础确实不扎实…拿了3天的时间写了一下kuangbin的第一个专题,在此自己总结一下,讲几道记忆深刻的题,首先记住,搜索问题中对于状态的抽离与遍历非常重要!

1.POJ 2251 Dungeon Master
与一般bfs的区别不大,就一个3维地图,6个方向,

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int d[6][3]={{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,1},{0,0,-1}};//记录方向移动带来的变化 
char map[35][35][35];//层 行 列
bool vis[35][35][35];
int l,r,c,sx,sy,sz,ex,ey,ez;
struct node{
	int x,y,z,step;
};
bool check(int x,int y,int z)
{
	if(x<0 || y<0 || z<0 || x>=l || y>=r || z>=c)
        return 1;
    else if(map[x][y][z] == '#')
        return 1;
    else if(vis[x][y][z])
        return 1;
    return 0;
}
int bfs()
{
	queue<node>q;
	node now,next;
	now.x=sx,now.y=sy,now.z=sz,now.step=0;
 	q.push(now),vis[now.x][now.y][now.z]=1;
 	while(q.size())
 	{
 		now=q.front();q.pop();
 		if(now.x==ex&&now.y==ey&&now.z==ez)return now.step;
 		for(int i=0;i<6;++i)
 		{
 			next=now;
			next.x=now.x+d[i][0];
			next.y=now.y+d[i][1];
			next.z=now.z+d[i][2];
			if(check(next.x,next.y,next.z))
                continue;
            vis[next.x][next.y][next.z] = 1;
            next.step = now.step+1;
            q.push(next);
		 }
	 }
	 return 0;
}
int main()
{
	while(scanf("%d%d%d",&l,&r,&c)&&(l+r+c))
	{
		for(int i=0;i<l;++i)
		{
			for(int j=0;j<r;++j)
			{
				scanf("%s",map[i][j]);
				for(int k=0;k<c;++k)
				{
					if(map[i][j][k]=='S')//记录起点 
					{
						sx=i,sy=j,sz=k;
					} 
					else if(map[i][j][k]=='E')
					{
						ex=i,ey=j,ez=k;
					}	//记录终点 
				}	
			}
		}
		memset(vis,0,sizeof(vis));//重新初始化 
		 int ans;
        ans = bfs();
        if(ans)
            printf("Escaped in %d minute(s).\n",ans);
        else
            printf("Trapped!\n");
	}
	return 0;
} 

2.C - Catch That Cow POJ - 3278
也是个裸的bfs,每个节点就3种状态 +1,-1,*2,目标位置小于的时候直接输出差值,这里我自己要注意的是,三个状态是并列的,每次取出来,一定要记住当前节点和往下的节点,三个都得跑一遍。

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=1e5+10; 
int d[maxn],n,k;bool v[maxn];
void bfs()
{
	queue<int>q;
	v[n]=1,d[n]=0,q.push(n);
	while(!q.empty())
	{
		int x=q.front();q.pop();
		if(x==k){printf("%d\n",d[k]);return;}
		int next;
		next=x+1;
		if(next<=100000&&next>=0&&!v[next])
		{
			d[next]=d[x]+1;
			v[next]=1;
			q.push(next);
		}
		next=x-1;
		if(next<=100000&&next>=0&&!v[next])
		{
			d[next]=d[x]+1;
			v[next]=1;
			q.push(next);
		}
		next=2*x;
		if(next<=100000&&next>=0&&!v[next])
		{
			d[next]=d[x]+1;
			v[next]=1;
			q.push(next);
		}
	}
}
int main()
{
	while(~scanf("%d%d",&n,&k))
	{
		if(n>k)printf("%d\n",n-k);
		else bfs();
	}
} 

3.D - Fliptile POJ - 3279
二进制状态搜索,当第一行决定了接下来的按键方法也就确定了。学会了二进制枚举的方法,n位是0~2^n-1,由于逆字典序,要反向枚举,取出第k位是i>>k&1

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=0x7FFFFFFF;
///Map存放棋盘,turn存放每个棋子(地板)翻转的状态(翻转几次)
///ans存放最后要输出的每个地板翻转的次数,cnt记录翻转地板的总次数
///res存放最小的翻转次数,M代表行数,N代表列数
int map[17][17],turn[17][17],ans[17][17],cnt,res,M,N;
int dir[5][2]={{0,0},{0,1},{1,0},{0,-1},{-1,0}};
int getcolor(int x,int y)
{
	int temp=map[x][y];
	for(int i=0;i<5;++i)
	{
		int xi=x+dir[i][0];
		int yi=y+dir[i][1];
		if(xi>=0&&xi<M&&yi>=0&&yi<N)temp+=turn[xi][yi];
	}
	return temp%2;
}
void dfs()
{
	for(int i=1;i<M;++i)
	{
		for(int j=0;j<N;++j)
		{
			if(getcolor(i-1,j))turn[i][j]=1,cnt++;
			if(cnt>res)return;
		}
	}
	for(int j=0;j<N;++j)
		if(getcolor(M-1,j))return;
	if(cnt<res)memcpy(ans,turn,sizeof(turn)),res=cnt;
}
int main()
{
	while(~scanf("%d%d",&M,&N))
	{
		res=maxn;
		for(int i=0;i<M;++i)
			for(int j=0;j<N;++j)
				scanf("%d",&map[i][j]);
		for(int i=0;i<(1<<N);++i)
		{
			cnt=0;
			memset(turn,0,sizeof(turn));	
			for(int j=0;j<N;++j)
			{
				turn[0][N-j-1]=i>>j&1;
				if(turn[0][N-j-1])cnt++;
			}
			dfs();
		}
		if(res==maxn)printf("IMPOSSIBLE\n");
		else
		{
			for(int i=0;i<M;++i)
			{
				for(int j=0;j<N;++j)
				{
					if(j>0)printf(" ");
					printf("%d",ans[i][j]);
				} 
				putchar('\n'); 
			}
		}	
	}
	return 0;
} 

E - Prime Path POJ - 3126
这题一开始我打表枚否质数判断是否只更新了一个数,但是这样复杂度太高了,看了网上题解才知道只要枚举4个位的数字就好了,学到了学到了,这样bfs转移充其量是40n的复杂度。

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
int prime[1000],d[10000],cnt=0,sx,ex;
bool v[10002],vis[10000];
using namespace std;
void primes()
{
	v[1]=v[0]=1;
	for(int i=2;i<=10000;++i)
	{
		if(!v[i])prime[++cnt]=i;
		for(int j=1;prime[j]*i<=10000&&j<=cnt;++j)
		{
			v[prime[j]*i]=1;
			if(i%prime[j]==0)
				break;
		}
	}
}
bool check(int e)
{
	if(v[e])return 0;
	if(vis[e])return 0;
	return 1;
}
int bfs()
{
	queue<int>q;
	q.push(sx),d[sx]=0,vis[sx]=1;
	int num,y;
	while(q.size())
	{
		int x=q.front();q.pop();
		if(x==ex){return d[x];}
		 int t[5];
        t[1]=x/1000;   ///记录千位数
        t[2]=x/100%10;  ///记录百位数
        t[3]=x/10%10;   ///记录十位
        t[4]=x%10;   ///记录各位
      
		for(int i=1;i<=4;++i)
		{
			int temp=t[i];
			for(int j=0;j<10;++j)
			{
				if(t[i]!=j)
				{
					t[i]=j;
					num=t[1]*1000+t[2]*100+t[3]*10+t[4];
				}
				if(num>=1000&&num<=9999&&check(num))
				{
					q.push(num);
					d[num]=d[x]+1;
					vis[num]=1;
				}
			}
			t[i]=temp;
		}
	}
	return -1;
}
int main()
{
	primes();
	int n;
	scanf("%d",&n);
	while(n--)
	{
		memset(vis,0,sizeof(vis));
		memset(d,0,sizeof(d));
		scanf("%d%d",&sx,&ex);
		int ans=bfs();
		if(ans!=-1)
		{
			printf("%d\n",ans);
		}
		else printf("Impossible\n");
	}
	return 0;
} 

G - Pots POJ - 3414
经典BFS倒水问题,关键是分清从一个节点转移到另一个节点有几种状态,通过这题还学会了记录路径,通过数字表示不同路径,每进一层路径++,由于每一个节点的父亲节点是唯一的。对于每个节点记录他自己的路径,所以开一个char[]

#include <iostream>
#include <queue>
#include <cstring>
 
using namespace std;
 
const int MAXN = 100;
int a, b, c;
bool vis[MAXN+1][MAXN+1];
struct node {
    int a, b, level;
    char path[MAXN+1];
    int plen;
};
string path[] = {
     "FILL(1)"
    ,"FILL(2)"
    ,"DROP(1)"
    ,"DROP(2)"
    ,"POUR(1,2)"
    ,"POUR(2,1)"
};
void output_result(int lvl, char p[], int n)
{
    cout << lvl << endl;
    for(int i=0; i<n; i++)
        cout << path[(int)p[i]] << endl;
}
void bfs()
{
    queue<node> q;
    memset(vis, true, sizeof(vis));
    node f;
    f.a = 0;
    f.b = 0;
    f.level = 0;
    memset(f.path, 0, sizeof(f.path));
    f.plen = 0;
    q.push(f);
 
    vis[f.a][f.b] = false;
 
    while(!q.empty()) {
        f = q.front();
        q.pop();
 
        if(f.a == c || f.b == c) {
            output_result(f.level, f.path, f.plen);
            return;
        }
        node v;
        v = f;
        v.level++;
        v.plen++;
        // FILL(a)
        if(a - f.a > 0) {
            v.a = a;
            v.b = f.b;
            if(vis[v.a][v.b]) {
                v.path[f.plen] = 0;
                q.push(v);
                vis[v.a][v.b] = false;
            }
        }
        // FILL(b)
        if(b - f.b > 0) {
            v.a = f.a;
            v.b = b;
            if(vis[v.a][v.b]) {
                v.path[f.plen] = 1;
                q.push(v);
                vis[v.a][v.b] = false;
            }
        }
        // DROP(a)
        if(f.a) {
            v.a = 0;
            v.b = f.b;
            if(vis[v.a][v.b]) {
                v.path[f.plen] = 2;
                q.push(v);
                vis[v.a][v.b] = false;
            }
        }
        // DROP(b)
        if(f.b) {
            v.a = f.a;
            v.b = 0;
            if(vis[v.a][v.b]) {
                v.path[f.plen] = 3;
                q.push(v);
                vis[v.a][v.b] = false;
            }
        }
        // POUR(a,b)
        if(f.a && (f.b < b)) {
            if(f.a > (b - f.b)) {
                v.a = f.a -(b - f.b);
                v.b = b;
            } else {
                v.a = 0;
                v.b = f.b + f.a;
            }
            if(vis[v.a][v.b]) {
                v.path[f.plen] = 4;
                q.push(v);
                vis[v.a][v.b] = false;
            }
        }
        // POUR(b,a)
        if(f.b && (f.a < a)) {
            if(f.b > (a - f.a)) {
                v.a = a;
                v.b = f.b -(a - f.a);
            } else {
                v.a = f.a + f.b;
                v.b = 0;
            }
            if(vis[v.a][v.b]) {
                v.path[f.plen] = 5;
                q.push(v);
                vis[v.a][v.b] = false;
            }
        }
    }
    cout << "impossible" << endl;
}
 
int main()
{
    cin >> a >> b >> c;
    bfs();
    return 0;
}

H - Fire! UVA - 11624
双重BFS,所有的火都是同一层树下,用time数组存储预处理出火跑的位置加入判断就好了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
 
using namespace std;
 
const int MAXN = 1005;
const int INF = 0x3f3f3f3f;
const int dx[] = {-1, 0, 0, 1};
const int dy[] = {0, -1, 1, 0};
 
struct node{
    node (int xx, int yy, int dd) {
        x = xx; 
        y = yy;
        d = dd;
    }
    int x, y, d;
};
 
char g[MAXN][MAXN];
int vf[MAXN][MAXN], vm[MAXN][MAXN], T[MAXN][MAXN];
int r, c, sx, sy;
 
queue<node> qf;
 
void bfs_fire() {
    while (!qf.empty()) {
        node u = qf.front(); 
        qf.pop(); 
        node v = u;
        for (int i = 0; i < 4; i++) {
            int tx = u.x + dx[i]; 
            int ty = u.y + dy[i];      
            if (tx < 0 || tx >= r || ty < 0 || ty >= c || g[tx][ty] == '#') continue;
            if (!vf[tx][ty]) {
                vf[tx][ty] = 1; 
                v.x = tx; 
                v.y = ty;
                v.d = u.d + 1;
                T[tx][ty] = v.d;
                qf.push(v);
            }  
        }
    }
}
 
int bfs_man() {
    queue<node> qm;
    while (!qm.empty()) qm.pop(); 
    node s(sx, sy, 0);
    qm.push(s);
    memset(vm, 0, sizeof(vm));
    vm[sx][sy] = 1;
    while (!qm.empty()) {
        node u = qm.front(); 
        qm.pop(); 
        if (u.x == 0 || u.x == r - 1 || u.y == 0 || u.y == c - 1)
            return u.d + 1;
        node v = u;
        for (int i = 0; i < 4; i++) {
            int tx = u.x + dx[i]; 
            int ty = u.y + dy[i]; 
            if (tx < 0 || tx >= r || ty < 0 || ty >= c || g[tx][ty] == '#') continue;
            if (u.d + 1 >= T[tx][ty]) continue;
            if (!vm[tx][ty]) {
                vm[tx][ty] = 1; 
                v.x = tx; 
                v.y = ty;
                v.d = u.d + 1;
                qm.push(v);
            }
        }
    }
    return -1;
}
 
int main() {
    int cas; 
    scanf("%d", &cas);
    while (cas--) {
        scanf("%d%d", &r, &c);
        for (int i = 0; i < r; i++)
            scanf("%s", g[i]);
 
        while(!qf.empty())  qf.pop();
        memset(vf, 0, sizeof(vf));
        memset(T, INF, sizeof(T));
 
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (g[i][j] == 'J') {
                    sx = i; 
                    sy = j; 
                }
                if (g[i][j] == 'F') {
                    node tmp(i, j, 0);       
                    vf[i][j] = 1;
                    T[i][j] = 0;
                    qf.push(tmp);
                } 
            } 
        }
 
        bfs_fire();
        int ans = bfs_man();    
        if (ans == -1) 
            printf("IMPOSSIBLE\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}

I - 迷宫问题 POJ - 3984
典型BFS,学到了回溯的时候用结点类型pre数组记录每一个节点的上一个节点的编号,从而回溯

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
struct node
{
	int x,y;
};
node pre[6][6];
int map[6][6];int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}},vis[6][6];
void print(node a)	//pre数组存储前一个结点递归回溯输出路径 
{
	if(a.x==0&&a.y==0)
		{
			printf("(0, 0)\n");
			return;	
		}
	print(pre[a.x][a.y]);
	printf("(%d, %d)\n",a.x,a.y);
}
void bfs()
{
	queue<node>q;
	node s,e;
	s.x=0,s.y=0,vis[0][0]=1;
	q.push(s);
	while(q.size())
	{
		s=q.front();q.pop();
		if(s.x==4&&s.y==4)print(s);
		for(int i=0;i<4;++i)
		{
			e.x=s.x+dir[i][0];
			e.y=s.y+dir[i][1];
			if(!vis[e.x][e.y]&&e.x<5&&e.x>=0&&e.y<5&&e.y>=0&&map[e.x][e.y]==0)
			{
				pre[e.x][e.y]=s;
				vis[e.x][e.y]=1;
				q.push(e);		
			}
		}
	}
}
int main()
{
	for(int i=0;i<5;++i)
		for(int j=0;j<5;++j)
	scanf("%d",&map[i][j]);
	bfs();
	return 0;
}

J - Oil Deposits HDU - 1241
DFS连通块消除标记,统计连通块的个数,没啥好讲的

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 100
using namespace std;
char a[N][N];
int n,m;
int dx[8]={0,0,1,1,1,-1,-1,-1};
int dy[8]={1,-1,0,-1,1,0,1,-1};//根据题目自己定义八个方向,有时候是定义四个方向 根据题目意思决定,此种类型题目都需自己定义方向
void dfs(int x,int y)
{
    int tempx,tempy;
    for(int i=0;i<8;i++)
    {
        tempx=x+dx[i];
        tempy=y+dy[i];
        if(tempx>=0&&tempy>=0&&tempx<n&&tempy<m&&a[tempx][tempy]=='@')//确定满足条件时进行搜索,否则跳出
        {
            a[tempx][tempy]='*';
            dfs(tempx,tempy);
        }
        else
            continue;
    }
}
int main()
{
    int count;
    while(cin>>n>>m,n!=0,m!=0)
    {
        count=0;
        for(int i=0;i<n;i++)
            cin>>a[i];
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(a[i][j]=='@')
                {
                    count++;
                    dfs(i,j);
                }
            }
        }
        cout<<count<<endl;
    }
    return 0;
}

剩下的题目大同小异,没啥好说的,就这样了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值