日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HDU - 5335 Walk Out(bfs+路径输出+贪心)

發(fā)布時間:2024/4/11 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HDU - 5335 Walk Out(bfs+路径输出+贪心) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

題目鏈接:點擊查看

題目大意:給出一個矩陣,要求從(1,1)點走到(n,m)點,要求途徑數(shù)字依次組成的二進制數(shù)字最小

題目分析:這個題我的心路歷程真的是非常坎坷的,就來稍微記錄一下吧,或許對以后做題能有幫助。

這樣一個中等難度的搜索題,從前一天中午做到了第二天中午,斷斷續(xù)續(xù)差不多思考+實現(xiàn)用了少說四個小時了,一直在思考,一直在優(yōu)化,最后A掉的那一刻感覺收獲也是蠻大的?。

因為整個迷宮只由0和1組成,所以我就覺得0的優(yōu)先級比1大,因為一個二進制中,盡可能的選0而不是1,能讓所組成的數(shù)小(這是當時一開始的錯誤思路)于是就去實現(xiàn),記錄路徑是用最暴力的方法,每個節(jié)點用string儲存以往的所有路徑,樣例過掉后當時還想多么簡單的一道題,交了之后MLE了,想了想,一共有1e6個點需要bfs,最壞情況每個節(jié)點的string都是存儲了之前的路徑,也就是1e6的長度,那么會有1e12的內(nèi)存開銷,于是想辦法優(yōu)化,因為每個節(jié)點的父親有且只有一個,所以我們可以用模擬鏈表的思想,用1e6的內(nèi)存開銷模擬一下鏈表,最后從后向前走過來也能得到答案,于是去交了一發(fā),發(fā)現(xiàn)又T掉了,我一開始用的是優(yōu)先隊列,時間復(fù)雜度總共是1e6*log1e6,差不多2e7左右。。可能是評測機跑得慢,沒莽過去,于是換思路,優(yōu)先隊列不行那就模擬優(yōu)先隊列的思想,用雙端隊列實現(xiàn)一下試試?也就是碰到0加入front,碰到1加入back,這樣時間復(fù)雜度也下降到了1e6,沒有了多余的log開銷,試了一下WA了,當時就覺得肯定是思路出問題了,就有點自閉了,但也不知道哪里出問題了,去網(wǎng)上找到了一個大牛的樣例,這里我挑比較典型的卡住我程序的幾個樣例放上來:

input:

5 5

00011

11111

00000

11111

00011

output:

100011

?這一組樣例就把我的程序卡掉了,我的程序輸出的是10000011,我模擬我的程序走了一遍,不是程序的問題,果然是思路的問題,我確實是以0優(yōu)先于1為權(quán)值判斷的,卻忘記了最重要的位數(shù)因素,那么接下來就需要再加一層bfs,分類討論一下:

  • 如果第一個數(shù)是1,那么直接搜索
  • 如果第一個數(shù)是0,那么搜索相連的0中最遠的那個,以最遠的0的位置為起點開始搜索
  • 這樣一來,就能保證答案的位數(shù)最小了,期間我還做了一點點的小優(yōu)化,就是在搜索的過程中不用每個方向都搜索,因為如果從起點到終點的過程中,盡可能的縮短路程才能達到縮短位數(shù)的目的,所以我們將搜索的四個方向改成兩個方向:向下和向右。這樣一定能讓最大位數(shù)不超過n+m-1位

    這樣上面那個樣例就簡簡單單的過掉了,這時候新的問題又來了:

    input:

    8 8

    10010010

    00101001

    10101100

    11000101

    11001101

    10001001

    11100011

    01010010

    output:

    100010000000010

    我的程序給出的答案是:100100000000010

    想了一陣子后,發(fā)現(xiàn)是雙端隊列的問題,就想上面說的,將0加入front,將1加入back,那么問題來了,被比較遠的0更新的1加入隊列后的位置比較偏后,而bfs在隊列中遍歷的順序是從前往后遍歷的,所以就導(dǎo)致了按理來說優(yōu)先級比較高的1的遍歷時間要比優(yōu)先級比較低的1的時間要晚,也就導(dǎo)致了每次更新的并不是最優(yōu)解

    實在是沒辦法了,去網(wǎng)上看了一下題解,一下子就有思路了,可以再開一個布爾類型的book數(shù)組,book[dis]里面維護的是相對于起點、曼哈頓距離為dis的點有沒有包含權(quán)值為0的點,如果有,那么當前距離下所有為1的點必定不是最優(yōu)解,所以就直接跳過了,這樣就可以保證從起點到達終點后經(jīng)過的路徑一定是最優(yōu)的,也就理所當然的解決了上面提到的問題,上面的樣例也是簡簡單單的過掉了

    最后就是考慮一下輸出了,因為我們用了數(shù)組來模擬鏈表儲存路徑,所以我們先用string倒序先將需要輸出的答案存起來,翻轉(zhuǎn)一下,去掉前導(dǎo)零,然后分情況輸出就好了,要么輸出0,要么輸出剩下的那一段字符串。

    這個題目如果只是看題解的話,會很簡單,但這個題對我來說真的學(xué)到了挺多東西,也是好久沒有靜下心來認真將一個題想這么久了,最后A掉的那一刻真的有點小成就感的,所以這篇博客稍微記錄一下對于這道題目思考的過程,下面放一下代碼吧,我感覺寫的有些亂,畢竟是改了又改的:

    #include<iostream> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e3+100;int n,m;int maze[N][N];bool vis[N][N];bool book[2*N];//記錄當前距離下有沒有0 const int b[4][2]={0,1,1,0,-1,0,0,-1};struct Node {int x,y;Node(){}Node(int X,int Y){x=X;y=Y;} }ans[N][N];//模擬鏈表記錄路徑vector<Node>pos;void bfs1()//搜索最優(yōu)解 {memset(book,false,sizeof(book)); memset(vis,false,sizeof(vis));queue<Node>q;if(!pos.empty())//如果第一個點是0,將所有距離起點最遠的點都壓入隊列中{for(int i=0;i<pos.size();i++){int x=pos[i].x;int y=pos[i].y;q.push(Node(x,y));vis[x][y]=true;book[x+y]=true;ans[x][y]=Node(-1,-1);//記得設(shè)置下限}}else//如果第一個點是1,只要將起點壓入就夠了{q.push(Node(0,0));vis[0][0]=true;ans[0][0]=Node(-1,-1);//設(shè)置下限}while(!q.empty()){Node cur=q.front();q.pop();if(cur.x==n-1&&cur.y==m-1){return;}if(maze[cur.x][cur.y]&&book[cur.x+cur.y])//如果當前坐標為1,但是當前距離已經(jīng)有0了,則肯定不是最優(yōu)解,直接忽略 continue;for(int i=0;i<2;i++)//兩個方向:向右和向下{int xx=cur.x+b[i][0];int yy=cur.y+b[i][1];if(xx<0||xx>=n||yy<0||yy>=m)continue;if(vis[xx][yy])continue;if(!maze[xx][yy])book[xx+yy]=true; vis[xx][yy]=true;ans[xx][yy]=Node(cur.x,cur.y);q.push(Node(xx,yy));}} }void bfs2()//搜索距離起點最遠的0 {memset(vis,false,sizeof(vis));queue<Node>q;q.push(Node(0,0));vis[0][0]=true;int dis=0;while(!q.empty()){Node cur=q.front();q.pop();for(int i=0;i<4;i++)//四個方向{int xx=cur.x+b[i][0];int yy=cur.y+b[i][1];if(xx<0||xx>=n||yy<0||yy>=m)continue;if(vis[xx][yy])continue;if(maze[xx][yy])continue;vis[xx][yy]=true;int diss=xx+yy;q.push(Node(xx,yy));if(diss>dis)//如果有距離起點更遠的0點,那之前存的點都清除,重新記錄{dis=diss;pos.clear();pos.push_back(Node(xx,yy));}else if(dis==diss)//如果有和最遠距離的距離相同的點,壓入vector中pos.push_back(Node(xx,yy));}} }void print() {string s;s+=to_string(maze[n-1][m-1]);for(Node i=ans[n-1][m-1];i.x!=-1&&i.y!=-1;i=ans[i.x][i.y])s+=to_string(maze[i.x][i.y]);reverse(s.begin(),s.end());int pos=0;while(pos<s.size()&&s[pos]!='1')pos++;if(s.empty()||pos==s.size())printf("0\n");else{for(int i=pos;i<s.size();i++)putchar(s[i]);putchar('\n');} } int main() {int w;cin>>w;while(w--){memset(ans,0,sizeof(ans));scanf("%d%d",&n,&m);for(int i=0;i<n;i++)for(int j=0;j<m;j++)scanf("%1d",&maze[i][j]);if(!maze[0][0])//如果第一位是0,則先找到最遠的0點的位置 bfs2();else//如果第一位不是0,則直接搜索 pos.clear();bfs1();//搜索最優(yōu)解print();//輸出 }return 0; }

    ?

    總結(jié)

    以上是生活随笔為你收集整理的HDU - 5335 Walk Out(bfs+路径输出+贪心)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。