NOJ寒假专题训练 Round #1 Search D~H题题解 另附A~C题解地址

比赛地址:http://acm.njupt.edu.cn/vjudge/contest/view.action?cid=171#overview


A, B, C三题见黄大仙博客。

A (NOJ 1203) :http://blog.csdn.net/u012968092/article/details/41975317

B (POJ 2362): http://blog.csdn.net/u012968092/article/details/41960293

C (POJ 1011): http://blog.csdn.net/u012968092/article/details/41960281


(以上题解版权归黄大仙所有)

接下来是D到H


D (POJ 2488)

基本可以见于所有算法竞赛教程中经典的骑士环游问题,DFS即可。

要注意的是棋盘上行号是用数字编号,列号是用字母编号的。

附代码:

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

#define maxn 27

struct node
{
	int x, y;
	node()	{}
	node(int _x,int _y)
	{
		x=_x;
		y=_y;
	}
} ans[maxn*maxn];

bool boc[maxn][maxn];
int p,q;
bool flg;
int tx[8]={-2,-2,-1,-1,1,1,2,2};//转移x行
int ty[8]={-1,1,-2,2,-2,2,-1,1};//转移y行

inline bool chck(int x, int y)	//判断这一格是否可以走 
{
	if (x<1||y<1||x>q||y>p)	return 0;
	if (boc[x][y])	return 0;
	return 1;
}

void dfs(int x, int y, int step)
{
	ans[step]=node(x,y);
	if (step==p*q-1)	{	flg = true;	return;	}
	for (int i=0;i<8;i++)
	if (chck(x+tx[i],y+ty[i]))
	{
		boc[x+tx[i]][y+ty[i]]=true;
		dfs(x+tx[i],y+ty[i],step+1);
		if (flg)	return;
		boc[x+tx[i]][y+ty[i]]=false;
	}
}

int main()
{
	int T;
	cin>>T;
	for (int s=1;s<=T;s++)
	{
		flg=false;
		printf("Scenario #%d:\n",s);
		memset(boc, 0, sizeof(boc));
		scanf("%d%d",&p,&q);
		for (int i=1;i<=q;i++)
			for (int j=1;(!flg)&&(j<=p);j++)
			{
				flg = false;
				boc[i][j]=true;
				dfs(i,j,0);
				boc[i][j]=false;
			}
		if (flg)
		{
			for (int i=0;i<p*q;i++)
				printf("%c%d", ans[i].x+'A'-1,ans[i].y);
			printf("\n\n");			
		}		
		else
			printf("impossible\n\n");
	}
}


E (POJ 3083)

难点在于怎么才能够沿着墙走。

通过画图(或者脑补)可以很容易的发现

假设↑  →  ↓  ←四种行进方式分别是0 1 2 3

那么当当前行进方向是0时 行进的优先级是 3 0 1 2

当当前行进方向是1时候 行进的优先级是 0 1 2 3

是2的时候 优先级为 1 2 3 0

是3的时候 优先级为2 3 0 1

那么剩下的问题就变得很简单了

附程序:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>

using namespace std;

const int maxn = 110;

int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0 , 1, 0,-1};

int dl[][2] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};	//顺时针转移 
int dr[][2] = {{0, 1}, {-1, 0}, {0, -1}, {1, 0}};	//逆时针转移 

int sx,sy,ex,ey;
int n, m;
char P[maxn][maxn];	//P为图 

struct node 
{
    int x,y,step;
    node()	{}
    node(int _x,int _y,int _step)
    {
		x=_x;
		y=_y;
		step=_step;
	}
};


int dfs(int x, int y, int d, int step, int dir[][2]) 
{
    for(int i=0; i<4; i++) 
	{
        int j=(d-1+4+i)%4;
        int nx=x+dir[j][0];
        int ny=y+dir[j][1];
        if(nx==ex && ny==ey) return step+1;	//到达目的地 
        if(nx < 0 || ny < 0 || nx > n || ny > m) continue;
        if(P[nx][ny]!='#')	return dfs(nx, ny, j, step+1, dir);
    }
}

int BFS(int sx,int sy) 	//宽度优先搜索 计算步数 
{
    bool vis[maxn][maxn];
    memset(vis,0,sizeof(vis));
	queue<node> Q;	
    Q.push(node(sx,sy,1));
    vis[sx][sy]=1;
    while(!Q.empty()) 
	{
        node p=Q.front(),tmp; Q.pop();
        if(p.x==ex && p.y==ey) return p.step;
        for(int d=0; d<4; d++) 
		{
            tmp.x=p.x+dx[d];
            tmp.y=p.y+dy[d];
            tmp.step=p.step+1;
            if(tmp.x<0||tmp.x>n||tmp.y<0||tmp.y>m) ;
            else
            	if(!vis[tmp.x][tmp.y] && (P[tmp.x][tmp.y]!='#'))
				{
	                vis[tmp.x][tmp.y]=1;
	                Q.push(tmp);
	            }
        }
    }
    return -1;
}

int main() 
{
    int T, d1, d2;
    cin>>T;
    while(T--) 
	{
        scanf("%d%d", &m, &n);
        for(int i=0; i<n; i++) 
		{
            scanf("%s", P[i]);
            for(int j=0;j<m;j++) 
			{
                if(P[i][j]=='S') 
					sx = i,sy = j;
                else 
					if(P[i][j]=='E')	ex=i,ey = j; 
            }
        }
        if(sx == 0) 		{ d1 = 3; d2 = 3; }
        else if(sx == n-1) 	{ d1 = 1; d2 = 1; }
        else if(sy == 0) 	{ d1 = 2; d2 = 0; }
        else if(sy == m-1) 	{ d1 = 0; d2 = 2; }
        printf("%d ", dfs(sx, sy, d1, 1, dl));
        printf("%d ", dfs(sx, sy, d2, 1, dr));
        printf("%d\n", BFS(sx, sy));
    }
    return 0;
}


F (POJ 3009)

就是一个裸的DFS搜索,唯一不同的便是每次是要碰到障碍物才会停下来并且障碍物会消失。

其他的就是暴力搜索不需要剪枝。特别要注意超过10便算作无解,输出-1。

附代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<set>
#include<algorithm>

using namespace std;

int p[30][30];
int w,h,sx,sy,ex,ey;
int ans;
int d[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
inline bool chck(int x,int y)
{
	return  (x>=0&&x<h&&y>=0&&y<w&&p[x][y]!=1)	;
}
struct node
{
    int x,y;
    int step;
    node ()	{}
    node(int _x,int _y,int _s)
    {
		x=_x;	y=_y;	step=_s;
	}
};

void DFS(int x,int y,int step)
{
	if(step>10)	return;
    for(int i=0;i<4;i++)
    {
        int nx,ny;
        bool ok=0;
        nx=x+d[i][0],ny=y+d[i][1];
        while	(chck(nx,ny))
        {
        	ok=1;
            if(nx==ex&&ny==ey&&step<ans)	ans=step;
            nx+=d[i][0],ny+=d[i][1];
        }
        if(p[nx][ny]==1&&ok)
        {
            p[nx][ny]=0;
            DFS(nx-d[i][0],ny-d[i][1],step+1);	//搜索后要把图恢复原状 
            p[nx][ny]=1;
        }
    }
}
int main()
{
    while(~scanf("%d%d",&w,&h)&&w&&h)
    {
        memset(p,0,sizeof p);
        int i,j;
        for(i=0;i<h;i++)
            for(j=0;j<w;j++)
			{
                scanf("%d",&p[i][j]);
                if(p[i][j]==2)	sx=i,sy=j;
                if(p[i][j]==3)	ex=i,ey=j;
            }
        ans=0x7ffffff;
        DFS(sx,sy,1);
        printf("%d\n",ans>10?-1:ans);  //超过10便输出-1
    }
}

G (POJ 1321)

就是经典的八皇后问题,只不过不允许部分点放置棋子。

暴力搜索求总数即可。

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

bool r[12],c[12];
bool p[12][12];
int tot,n,m;

void dfs(int x,int y, int t)	//x,y表示当前位置 t表示这是第几个放置的棋子
{
	if (t==m)
	{
		tot++;
		return;
	}
	for (int i=x;i<n;i++)
		for (int j=(i==x?y:0);j<n;j++)  //如果是x所在行,从y开始,否则从0开始查找
			if (p[i][j]&&!r[i]&&!c[j])
			{
				r[i]=c[j]=true;
				dfs(i,j,t+1);
				r[i]=c[j]=false;
			}
}

int main()
{
	while (scanf("%d%d", &n, &m)!=EOF&&!(n==-1&&m==-1))
	{
		char ch;
		memset(p,0,sizeof p);
		getchar();
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				ch = getchar();
				if (ch == '#')
					p[i][j]=1;
				else
					p[i][j]=0;
			}
			getchar();
		}
		memset(r,0,sizeof r);	memset(c,0,sizeof c);
		tot=0;
		dfs(0,0,0);
		printf("%d\n",tot);
	}
}

H (POJ 3278)

因为要求最小步数,所以这里宽度优先搜索会更适合一些。

要注意的是每次得到一个中间结果,就要记录下这个结果已经被扫描过了,下次遇到就不会把它加入队列中,这样便可以保证时间复杂度为O(n) 。

附代码:

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

struct node
{
	int nm;
	int step;
	node()	{}
	node (int _nm,int _step)
	{
		nm=_nm;
		step=_step;
	}
}t;
queue <node> Q;
bool bom[100100];
int n,k;
int main()
{
	memset(bom,0,sizeof bom);
	int ans=-1;
	scanf("%d%d",&n,&k);
	Q.push(node(n,0));
	while ( (ans==-1)&&!Q.empty())	//利用队列先进先出的特性来进行BFS 
	{
		t=Q.front();
		if (t.nm==k) {	ans=t.step;	break;	} //如果找到答案便把答案记入ans并退出循环
		if (t.nm+1<=100000&&!bom[t.nm+1])	{	Q.push(node(t.nm+1,t.step+1));	bom[t.nm+1]=1;	}
		if (t.nm*2<=100000&&!bom[t.nm*2])	{	Q.push(node(t.nm*2,t.step+1));	bom[t.nm*2]=1;	}
		if (t.nm-1>=0&&!bom[t.nm-1])		{	Q.push(node(t.nm-1,t.step+1));	bom[t.nm-1]=1;	}
		Q.pop();
	}
	printf("%d\n",ans);
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值