比赛地址: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);
}