POJ 3009(冰壶问题)

题目链接:https://vjudge.net/problem/POJ-3009
冰壶问题,又是一道经典dfs问题。思路也比较明确,一开始采取全局变量控制x,y,,代码如下
#include
#include
using namespace std;

int a[25][25];
int q,w,startq,startw,times = 0;
int nowx ,nowy;
int mins = 11;
int gox[4] = {-1,1,0,0};
int goy[4] = {0,0,-1,1};

int go(int i){
if(nowx+gox[i]>=0&&nowx+gox[i]=0&&nowy+goy[i] nowx = nowx + gox[i];nowy = nowy + goy[i];times++;
return 1;
}
else return 0;
}

void dfs(){
for(int i = 0;i<4;i++){
if(times>10)return ;
if (go(i)){
cout< if(a[nowx][nowy]==1){
a[nowx][nowy]=0;
nowx = nowx-gox[i];
nowy = nowy-goy[i];
if(a[nowx][nowy]==3){mins = min(mins,times);a[nowx+gox[i]][nowy+goy[i]] = 1;return ;}
else if(a[nowx][nowy]==0){
dfs();
a[nowx+gox[i]][nowy+goy[i]] = 1;
return ;
}
}
if(a[nowx][nowy]==0){
i--;
dfs();
nowx = nowx-gox[i];nowy = nowy-goy[i];times--;
}
}
else{
if(a[nowx][nowy]==3){mins = min(mins,times);return ;}
if(a[nowx][nowy]==0){dfs();}
}
}
}

int main(){
while(cin>>w>>q&&q&&w){
mins = 11;
times = 0;
memset(a,0,sizeof(a));
for(int k=0;k for(int l=0;l cin>>a[k][l];
if(a[k][l]==2){startq = k;startw = l;nowx = k;nowy = l;}
}
}
cout< dfs();
cout< }
return 0;
}
很快发现了很致命的问题,,一个是关于墙壁边界的判断,因为越界很不好写,还有如果有连续两次移动一步就停止,就会没有办法回溯。。。。

于是在思考怎么控制边界,,突然想到若是把数组开大一点,然后前后都留空,留空的位置值设为-1,这样访问时不会越界,而且可以很方便判断边界,,于是变为:
memset(a,-1,sizeof(a));
然后再思索:前进的路程上由于连续两次走一步就停会让回溯很麻烦,,干脆将x,y设为局部变量然后先进行移动判断是否下一步走后符合要求再进行移动,若不符合也可以很方便的就变为原来的位置。而且注意求的不是移动步数,而是投掷次数(停止次数)。
还有一个点要注意,就是只有在移动过程中碰到1才可以把他变为0,若是静止状态下不能向1的方向走,那同样是1,怎么去判断到底是不是移动状态呢,有一个很巧妙的办法:
void dfs(int x,int y){
times++;
if(times>10)return;
for(int i = 0;i<4;i++){
int nx = x+gox[i];
int ny = y+goy[i];
if(a[nx][ny]==1)continue;
while(a[nx][ny]==2||a[nx][ny]==0){
nx+=gox[i];ny+=goy[i];
}
if(a[nx][ny]<0)continue;
if(a[nx][ny]==1){
a[nx][ny] = 0;
dfs(nx-gox[i],ny-goy[i]);
times--;
a[nx][ny] = 1;
}
if(a[nx][ny]==3){
mins = min(times,mins);
continue;
}
}
}
如果是移动状态则在其遇到1前必会遇到其他数字的地板,这时就将对移动状态的1判断放在整个判断的最后。而静止放在最前,如果遇到静止直接continue出去。完美解决。

总结一下:本题难点在墙壁的边界判断,以及两个地板1的不同处理上。注意数组录入数据下标不要从0开始,保证第一个数据前有空间可以设置边界从而不会访问越界!!!
代码如下:
#include
#include
using namespace std;

int a[25][25];
int q,w,startq,startw,times = 0;
int mins = 11; //最大值不会超过10
int gox[4] = {-1,1,0,0};//x方向移动
int goy[4] = {0,0,-1,1};//y方向移动

void dfs(int x,int y){
times++;
if(times>10)return;
for(int i = 0;i<4;i++){
int nx = x+gox[i];
int ny = y+goy[i];
if(a[nx][ny]==1)continue;//静止状态1的判断
while(a[nx][ny]==2||a[nx][ny]==0){//0和2地板等效,都继续向前走
nx+=gox[i];ny+=goy[i];
}
if(a[nx][ny]<0)continue;//到了边界就放弃这种移动,并不改变x,y的值
if(a[nx][ny]==1){//移动状态1的判断,同时进行回溯法。
a[nx][ny] = 0;
dfs(nx-gox[i],ny-goy[i]);
times--;
a[nx][ny] = 1;
}
if(a[nx][ny]==3){
mins = min(times,mins);
continue;
}
}
}

int main(){
while(cin>>w>>q&&q&&w){
mins = 11;
times = 0;
memset(a,-1,sizeof(a));
for(int k=1;k<=q;k++){
for(int l=1;l<=w;l++){
cin>>a[k][l];
if(a[k][l]==2){startq = k;startw = l;}//确定起始位置
}
}
dfs(startq,startw);
if(mins==11)cout<<"-1"< else cout< }
return 0;
}

157ms

你可能感兴趣的:(POJ 3009(冰壶问题))