題目1335:闖迷宮(40分)

http://ac.jobdu.com/problem.php?id=1335php


題目描述:

sun所在學校每一年都要舉行電腦節,今年電腦節有一個新的趣味比賽項目叫作闖迷宮。
sun的室友在幫電腦節設計迷宮,因此室友就請sun幫忙計算下走出迷宮的最少步數。
知道了最少步數就能夠輔助控制比賽難度以及去掉一些沒有路徑到達終點的map。
比賽規則是:從原點(0,0)開始走到終點(n-1,n-1),只能上下左右4個方向走,只能在給定的矩陣裏走。ios

輸入:

輸入有多組數據。
每組數據輸入n(0<n<=100),而後輸入n*n的01矩陣,0表明該格子沒有障礙,爲1表示有障礙物。
注意:若是輸入中的原點和終點爲1則這個迷宮是不可達的。算法

輸出:

對每組輸入輸出該迷宮的最短步數,若不能到達則輸出-1。測試

樣例輸入:
2
0 1
0 0
5
0 0 0 0 0
1 0 1 0 1
0 0 0 0 0
0 1 1 1 0
1 0 1 0 0
樣例輸出:
2
8
一開始沒認真看題目,覺得是最簡單的只能向下和向右兩個方向走,因而簡單的打表遞推提交經過了10個測試用例裏的4個,看到題目中的是能夠向四個方向移動的,既然一開始考慮使用狀態轉移就堅持一條道走到黑了,這裏存在的問題是不知足dp裏的無後效性,不太好找到一個合適的方向進行計算,由於當前節點的值會依賴4個相鄰的結點,彷佛一趟掃描基本不太可能,不過最近實現一篇論文的時候用到了  fast sweeping算法,具體的思想也是從一些肯定點source的效果逐級擴散到其餘點(這裏本質上很類似,並且不用記錄到達每個狀態的細節信息),一開始簡單的表遞推的時候從四個方向掃描,最後再加上一趟從左上到右下的掃描結果經過10個裏面的9個(其實只是隨便試一試,明顯感受算法在邏輯上仍是有問題的(由於更新的時候始終只考慮兩個相鄰的兩個點),說明測試用例並非太強),如下是代碼:     寫到後面才發現代碼中有致命問題的, 由於下標爲0的兩列值再初始肯定後就固定了不在更新了,囧

#include <iostream>
#include <iomanip>
#include <cmath>
//#include <map>
#include <assert.h>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
 
const int MAXDIST = 10000;
int map[105][105];
int dist[105][105];
void InitDist( int n)
{
     for ( int i=0; i<=n; i++) //多取一位
         for ( int j=0; j<=n; j++)
             dist[i][j]=MAXDIST;
}
int computePath( int n)
{
     if (map[0][0]==1 || map[n-1][n-1]==1)
         return -1;
     InitDist(n);
     dist[0][0]=0;
     for ( int i=1; i<n; i++)
         if (0 == map[0][i])
             dist[0][i]=dist[0][i-1]+1;
     
     for ( int i=1; i<n; i++)
         if (0==map[i][0])
             dist[i][0]=dist[i-1][0]+1;
 
 
     for ( int i=1; i<n; i++)
         for ( int j=1; j<n; j++) //紅色部分爲問題所在,後面相似
         {
             if (0 == map[i][j])
             {
                 int temp1 = dist[i-1][j]+1;
                 int temp2 = dist[i][j-1]+1;
                 int temp = temp1<temp2 ? temp1:temp2;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }  
         }
 
     for ( int i=1; i<n; i++)
         for ( int j=n-1; j>0; j--)
             if (0 == map[i][j])
             {
                 int temp1 = dist[i-1][j]+1;
                 int temp2 = dist[i][j+1]+1;
                 int temp = temp1<temp2 ? temp1:temp2;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }
 
     for ( int i=n-1; i>0; i--)
         for ( int j=1; j<n; j++)
             if (0 == map[i][j])
             {
                 int temp1 = dist[i+1][j]+1;
                 int temp2 = dist[i][j-1]+1;
                 int temp = temp1<temp2 ? temp1:temp2;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }
 
     for ( int i=n-1; i>0; i--)
         for ( int j=n-1; j>0; j--)
             if (0 == map[i][j])
             {
                 int temp1 = dist[i+1][j]+1;
                 int temp2 = dist[i][j+1]+1;
                 int temp = temp1<temp2 ? temp1:temp2;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }
 
     for ( int i=1; i<n; i++)
         for ( int j=1; j<n; j++)
         {
             if (0 == map[i][j])
             {
                 int temp1 = dist[i-1][j]+1;
                 int temp2 = dist[i][j-1]+1;
                 int temp = temp1<temp2 ? temp1:temp2;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }  
         }
     if (dist[n-1][n-1]>=MAXDIST)
         return -1;
     else
         return dist[n-1][n-1];
}
int main()
{
     //freopen("in.txt","r",stdin);
     int n;
     while (cin>>n)
     {
         for ( int i=0; i<n; i++)
             for ( int j=0; j<n; j++)
                 cin>>map[i][j];
         cout<<computePath(n)<<endl;
     }
     return 0;
}



糾正上面提到的問題果真AC了, 沒有第五趟額外的掃描代碼經過不了,因此暫時還不肯定代碼絕對正確,第五趟是否必定不會再有值更新並沒有把握,之後再仔細考慮吧,不事後面寫的直到徹底沒值更新了才中止掃描(只朝一個方向掃描只爲代碼簡潔,收斂速度會慢一些吧)160ms-》190ms
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <cmath>
//#include <map>
#include <assert.h>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
  
const int MAXDIST = 10000;
int map[105][105];
int dist[105][105];
void InitDist( int n)
{
     for ( int i=0; i<=n; i++) //多取一位 從0取才有意義
         for ( int j=0; j<=n; j++)
             dist[i][j]=MAXDIST;
}
int SourceForm( int x, int y, int n)
{
     if (x<0 || x>=n || y<0 || y>=n)
         return MAXDIST;
     else
         return dist[x][y];
}
 
int computePath( int n)
{
     if (map[0][0]==1 || map[n-1][n-1]==1)
         return -1;
     InitDist(n);
     dist[0][0]=0;
     
 
     for ( int i=0; i<n; i++)
         for ( int j=0; j<n; j++)
             if (0 == map[i][j])
             {
                 int temp = SourceForm(i-1,j,n)+1;
                 if (SourceForm(i,j-1,n)+1<temp)
                     temp = SourceForm(i,j-1,n)+1;
                 if (SourceForm(i+1,j,n)+1<temp)
                     temp = SourceForm(i+1,j,n)+1;
                 if (SourceForm(i,j+1,n)+1<temp)
                     temp = SourceForm(i,j+1,n)+1;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }
 
     for ( int i=0; i<n; i++)
         for ( int j=n-1; j>=0; j--)
             if (0 == map[i][j])
             {
                 int temp = SourceForm(i-1,j,n)+1;
                 if (SourceForm(i,j-1,n)+1<temp)
                     temp = SourceForm(i,j-1,n)+1;
                 if (SourceForm(i+1,j,n)+1<temp)
                     temp = SourceForm(i+1,j,n)+1;
                 if (SourceForm(i,j+1,n)+1<temp)
                     temp = SourceForm(i,j+1,n)+1;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }
 
     for ( int i=n-1; i>=0; i--)
         for ( int j=0; j<n; j++)
             if (0 == map[i][j])
             {
                 int temp = SourceForm(i-1,j,n)+1;
                 if (SourceForm(i,j-1,n)+1<temp)
                     temp = SourceForm(i,j-1,n)+1;
                 if (SourceForm(i+1,j,n)+1<temp)
                     temp = SourceForm(i+1,j,n)+1;
                 if (SourceForm(i,j+1,n)+1<temp)
                     temp = SourceForm(i,j+1,n)+1;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }
 
     for ( int i=n-1; i>=0; i--)
         for ( int j=n-1; j>=0; j--)
             if (0 == map[i][j])
             {
                 int temp = SourceForm(i-1,j,n)+1;
                 if (SourceForm(i,j-1,n)+1<temp)
                     temp = SourceForm(i,j-1,n)+1;
                 if (SourceForm(i+1,j,n)+1<temp)
                     temp = SourceForm(i+1,j,n)+1;
                 if (SourceForm(i,j+1,n)+1<temp)
                     temp = SourceForm(i,j+1,n)+1;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }
 
     for ( int i=0; i<n; i++)
         for ( int j=0; j<n; j++)
             if (0 == map[i][j])
             {
                 int temp = SourceForm(i-1,j,n)+1;
                 if (SourceForm(i,j-1,n)+1<temp)
                     temp = SourceForm(i,j-1,n)+1;
                 if (SourceForm(i+1,j,n)+1<temp)
                     temp = SourceForm(i+1,j,n)+1;
                 if (SourceForm(i,j+1,n)+1<temp)
                     temp = SourceForm(i,j+1,n)+1;
                 if (temp<dist[i][j])
                     dist[i][j]=temp;
             }
 
     if (dist[n-1][n-1]>=MAXDIST)
         return -1;
     else
         return dist[n-1][n-1];
}
 
 
int main()
{
     //freopen("in.txt","r",stdin);
     int n;
     while (cin>>n)
     {
         for ( int i=0; i<n; i++)
             for ( int j=0; j<n; j++)
                 scanf ( "%d" ,&map[i][j]);
                 //cin>>map[i][j];
         cout<<computePath(n)<<endl;
     }
     return 0;
}
/**************************************************************
     Problem: 1335
     User: xjbscut
     Language: C++
     Result: Accepted
     Time:160 ms
     Memory:1600 kb
****************************************************************/









以爲傳遞思想絕對是OK的,使用了一種以爲嚴謹且暴力保證收斂的手段(每次都考慮四個緊鄰,把相鄰元素是否越界 封裝在SourceForm(int x,int y,int n)中很優雅
#include <iostream>
#include <iomanip>
#include <cmath>
//#include <map>
#include <assert.h>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
  
const int MAXDIST = 10000;
int map[105][105];
int dist[105][105];
void InitDist( int n)
{
     for ( int i=0; i<=n; i++) //多取一位 從0取才有意義
         for ( int j=0; j<=n; j++)
             dist[i][j]=MAXDIST;
}
int SourceForm( int x, int y, int n)
{
     if (x<0 || x>=n || y<0 || y>=n)
         return MAXDIST;
     else
         return dist[x][y];
}
int computePath( int n)
{
     if (map[0][0]==1 || map[n-1][n-1]==1)
         return -1;
     InitDist(n);
     dist[0][0]=0;
     bool hasUpadated;
    
     do
     {
         hasUpadated = false ;
         for ( int i=0; i<n; i++)
             for ( int j=0; j<n; j++)
                 if (0 == map[i][j])
                 {
//考慮四個相鄰元素
                     int temp = SourceForm(i-1,j,n)+1;
                     if (SourceForm(i,j-1,n)+1<temp)
                         temp = SourceForm(i,j-1,n)+1;
                     if (SourceForm(i+1,j,n)+1<temp)
                         temp = SourceForm(i+1,j,n)+1;
                     if (SourceForm(i,j+1,n)+1<temp)
                         temp = SourceForm(i,j+1,n)+1;
                     if (temp<dist[i][j])
                     {
                         dist[i][j]=temp;
                         hasUpadated =true;
                     }
                 }
 
     } while (hasUpadated);
 
 
     if (dist[n-1][n-1]>=MAXDIST)
         return -1;
     else
         return dist[n-1][n-1];
}
int main()
{
     //freopen("in.txt","r",stdin);
     int n;
     while (cin>>n)
     {
         for ( int i=0; i<n; i++)
             for ( int j=0; j<n; j++)
                 scanf ( "%d" ,&map[i][j]);
                 //cin>>map[i][j];
         cout<<computePath(n)<<endl;
     }
     return 0;
}
/**************************************************************
     Problem: 1335
     User: xjbscut
     Language: C++
     Result: Accepted
     Time:190 ms
     Memory:1596 kb
****************************************************************/


其實撇開這種遞推的思路,使用DFS是這種問題的最直觀的思路,可是搜索算法相對來講比較難寫好,特別使得處理好剪枝,不然很容易超時,能夠考慮的是搜索的過程當中記錄到每個中間點的當前最短消費(而不是隻存儲目標點的信息,到了目標節點才進行實際的消費比較),這樣能夠減小大量沒必要要的搜索,不過即便這樣仍是超時,其中第五個測試樣例超過了1s,前面經過的樣例都只有20ms左右)估計是一個比較強的測試用例,想了下加了個剪紙就AC了,不過期間在AC結果裏面仍是比較長的(480ms)
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <map>
#include <assert.h>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
using namespace
相關文章
相關標籤/搜索