国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

HDOJ.1010 Tempter of the Bone (DFS)

2019-11-14 10:25:07
字體:
供稿:網(wǎng)友

Tempter of the Bone [從零開始DFS(1)]

從零開始DFS HDOJ.1342 Lotto [從零開始DFS(0)] — DFS思想與框架/雙重DFS HDOJ.1010 Tempter of the Bone [從零開始DFS(1)] —DFS四向搜索/奇偶剪枝 HDOJ(HDU).1015 Safecracker [從零開始DFS(2)] —DFS四向搜索變種 HDOJ(HDU).1016 PRime Ring Problem (DFS) [從零開始DFS(3)] —小結(jié):做DFS題目的關(guān)注點 HDOJ(HDU).1035 Robot Motion [從零開始DFS(4)]—DFS題目練習(xí) HDOJ(HDU).1241 Oil Deposits(DFS) [從零開始DFS(5)] —DFS八向搜索/雙重for循環(huán)遍歷 HDOJ(HDU).1258 Sum It Up (DFS) [從零開始DFS(6)] —DFS雙重搜索/去重技巧 HDOJ(HDU).1045 Fire Net [從零開始DFS(7)]—DFS練習(xí)/check函數(shù)的思想

題意分析

給出一張大小為n * m的地圖,其中: X代表墻壁,走不通; S代表起點; D代表終點; . 代表空白區(qū)域,即可以走通。 求解是否正好可以在第T步的時候走到終點,并且不能走回頭路(即走過的地方不能再走)。

典型的地圖搜索問題嘛,首先想到的就是DFS。先回顧一下上次探討的內(nèi)容。 傳送門:

HDOJ.1342 Lotto [從零開始DFS(0)]

1.幾個名詞 DFS的搜索原則 遞歸 剪枝 遞歸邊界

2.DFS的函數(shù)模型

void dfs( 參數(shù) ){ // 遞歸邊界 // 可以是檢查是否滿足解的要求 // 完成某系列動作 // 繼續(xù)遞歸調(diào)用dfs}

3.其實上次有2個問題沒有討論的太明白

(1) Q:沒有進(jìn)行排序,為什么自動是字典序?

A:因為題目給的數(shù)據(jù),即數(shù)組a本來就是按照升序排好的,按照上次說的搜索原則,搜索到的解,一定是按照遞增排列好的。每次遞歸調(diào)用dfs的時候,都是先假定選取這個數(shù)字,然后再看不選這個數(shù)字的情況,那么選取的這個數(shù)字,肯定比不選這個數(shù)字以后的情況來的小,所以他的下一個解,一定比這個解的字典序要大。如:

位置 1 2 ……
a數(shù)組 1 2 ……
b數(shù)組 1 待定 ……

若現(xiàn)在b的第二個位置選了2

位置 1 2 ……
a數(shù)組 1 2 ……
b數(shù)組 1 2 ……

那么這組解就是12XXXX

若不選2呢?

位置 1 2 ……
a數(shù)組 1 2 ……
b數(shù)組 1 待定(3、4等都有可能) ……

那么這組解就有可能是: 13XXXX 或者 14XXXX 自然字典序就比上面的情況來的大。

(2) Q: 在遞歸調(diào)用的時候,如何判斷這個節(jié)點(或者數(shù)組的某個元素)已經(jīng)訪問過了呢?

A:因為上次的題是一維數(shù)組并且按順序訪問的,就不用檢查是否訪問過了。但是這個題就需要檢查了,一般采用的是visit數(shù)組。

先上代碼,掰開了揉碎了慢慢來。。

代碼總覽

/* Title:HDOJ.1010 Author:pengwill Date:2017-2-4*/#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int n,m,t,x1,y1,x2,y2;int spx[] = {1,0,-1,0};int spy[] = {0,1,0,-1};char mp[10][10];bool visit[10][10];bool judge = false;void init(){ for(int i = 0;i<n;++i){ for(int j = 0;j<m ;++j){ if(mp[i][j] == 'S') {x1 = i;y1 = j;} else if(mp[i][j] == 'D') {x2 = i;y2 = j;} else if(mp[i][j] == 'X') visit[i][j] = true; } }}bool check(int x, int y){ if(x<0||x>=n||y<0||y>=m) return false; else return true;}void dfs(int x, int y, int s){ if(check(x, y) == false)return; visit[x][y] = true; if(x==x2 && y == y2 && s == t){ judge = true; return; } //int temp = t-((abs(x1-x2) + abs(y1-y2))); //if(temp<0 ||temp %2 ==1) return; if(((t-s)%2 != (abs(x-x2) + abs(y-y2)) %2)) return; for(int i = 0;i<4;++i){ if(!visit[x+spx[i]][y+spy[i]]&&!judge){ dfs(x+spx[i],y+spy[i],s+1); visit[x+spx[i]][y+spy[i]] = false; } }}int main(){ // freopen("in.txt","r",stdin); while(scanf("%d%d%d",&n,&m,&t) &&(n+m+t)){ memset(mp,0,sizeof(mp)); memset(visit,0,sizeof(visit)); for(int i = 0;i<n;++i) scanf("%s",mp[i]); init(); judge = false; dfs(x1,y1,0); if(judge) printf("YES/n"); else printf("NO/n"); } return 0;}

首先是main函數(shù)

while(scanf("%d%d%d",&n,&m,&t) &&(n+m+t)){ memset(mp,0,sizeof(mp)); memset(visit,0,sizeof(visit)); for(int i = 0;i<n;++i) scanf("%s",mp[i]); init(); judge = false; dfs(x1,y1,0); if(judge) printf("YES/n"); else printf("NO/n"); }

按照題意讀取nmt三個數(shù)據(jù),并且初始化地圖數(shù)組mp檢查是否訪問過的數(shù)組visist,之后讀入地圖的數(shù)據(jù),接著是init函數(shù),看看init做了什么。

void init(){ for(int i = 0;i<n;++i){ for(int j = 0;j<m ;++j){ if(mp[i][j] == 'S') {x1 = i;y1 = j;} else if(mp[i][j] == 'D') {x2 = i;y2 = j;} else if(mp[i][j] == 'X') visit[i][j] = true; } }}

原來是找到起點S和終點D的位置,并且把墻所在坐標(biāo)的visit寫為true,代表訪問過。不難理解,既然走不通,就把它變成訪問過,只要在dfs過程中稍加判斷也不會再次訪問了。

回到main函數(shù),接著是吧judge改為false,這個是遞歸邊界。題目只要求找出能還是不能,如果我搜索到某一情況,判定可以走到終點,那么明顯就沒有必要繼續(xù)搜索下去了。接下來是重頭戲,dfs函數(shù)。

void dfs(int x, int y, int s){ if(check(x, y) == false)return;//判斷是否出界 visit[x][y] = true;//改變當(dāng)前節(jié)點為訪問過 if(x==x2 && y == y2 && s == t){//判斷是否滿足題目要求的解 judge = true; return; } if(((t-s)%2 != (abs(x-x2) + abs(y-y2)) %2)) return;//奇偶剪枝 if(t-s-abs(x-x2)-abs(y-y2)<0) return;//步數(shù)不夠剪枝 for(int i = 0;i<4;++i){//遞歸調(diào)用dfs if(!visit[x+spx[i]][y+spy[i]]&&!judge){ dfs(x+spx[i],y+spy[i],s+1); visit[x+spx[i]][y+spy[i]] = false; } }}

首先dfs有3個形式參數(shù),xy代表入口坐標(biāo),即搜索入口,s代表走的步數(shù)。首先按照check函數(shù),檢查當(dāng)前坐標(biāo)是否超出地圖邊界,若超出,則終止遞歸。

check函數(shù):

bool check(int x, int y){ if(x<0||x>=n||y<0||y>=m) return false; else return true;}

接著把當(dāng)前的位置,變?yōu)樵L問過。緊接著就是判斷當(dāng)前坐標(biāo)是否就是終點坐標(biāo)并且看步數(shù)是否可題目要求的一致,若是的話,judge變?yōu)閠rue表示找到解了,以后遞歸都沒必要做了。 然后是奇偶剪枝

這里寫圖片描述 (圖2-1)

Tip:只能上下左右走,不能斜著走。

如圖2-1所示,由start到end圖中最短路徑為紅色,走了12步。相比于紅色路徑,藍(lán)色路徑大費周折,大家可以數(shù)一下,共走了14步。此時步數(shù)的偏移量為14-12=2。奇偶剪枝,就是步數(shù)的偏移量永遠(yuǎn)為偶數(shù)(可證明),因此可以得出:最短路徑步數(shù)+某一偶數(shù)(即偏移量)=某一可行解歩數(shù)。(下面這句是重點)即最短路歩數(shù)和某一可行解歩數(shù)的奇偶性相同

回到題目,若已知最短路徑歩數(shù)為偶數(shù),且在某一情況下剩余的步數(shù)為奇數(shù),那么無論如何也是無法恰好到達(dá)終點的,因此這種情況就不用算啦,終止遞歸。當(dāng)然步數(shù)不夠,也是走不到終點的。

接著遞歸調(diào)用dfs,這里有新東西啦,visit[x+spx[i]][y+spy[i]]是什么鬼。

看一下開頭定義的的:

int spx[] = {1,0,-1,0};int spy[] = {0,1,0,-1};

不難發(fā)現(xiàn),spx為0時spy為±1,反之成立。也可以想到,x±1和y±1分別對應(yīng)的就是地圖上,上下左右移動一個單位。這里是個小trick,用這樣一個的數(shù)組來實現(xiàn)上下左右的移動,畢竟一個for循環(huán)多好寫。 當(dāng)且僅當(dāng)下一個位置沒有訪問過,并且這個時候還沒有找到可行解時,遞歸調(diào)用dfs,在調(diào)用結(jié)束后,記得要把visit[x+spx[i]][y+spy[i]]置為false。原因很簡單,不能影響到下一次循環(huán)呀:對于下一次循環(huán)[x+spx[i]][y+spy[i]]這個位置,可是沒有訪問過的。(無后效性??請各位指點)。 這樣一道題,就ac了。

多做題,加緊訓(xùn)練吧!


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 通榆县| 商洛市| 仁布县| 高清| 阳信县| 辉南县| 建始县| 贵阳市| 陆河县| 丰原市| 拉萨市| 德惠市| 雷山县| 南通市| 呈贡县| 盐山县| 宁津县| 宣武区| 乌兰浩特市| 桃园市| 达孜县| 鄂州市| 华容县| 清远市| 敦化市| 莱芜市| 寿阳县| 汾西县| 深水埗区| 靖州| 石首市| 油尖旺区| 昌江| 连城县| 得荣县| 平安县| 沁源县| 昂仁县| 广昌县| 南平市| 射阳县|