1 汽車站的設計

2021-08-30 06:27:08 字數 4385 閱讀 1610

題目抽象:

乙個有向圖有n個點(n<=50),g條邊(g<=4000)。現在要求刪掉最少的點,使得不存在從1號點到n號點的長度<=m的路徑(m<1000)。當然,不能刪除1號點或者n號點,輸出最少需要刪除的點數(刪除乙個點的時候會連帶將與之相連的邊也刪除)。

解題思路:

最樸素的搜尋方法是列舉所有刪除點的方案,每找到以一種方案,就求一次從1號點到n號點的最短路徑,看看長度是否超過m,每個點有刪除和不刪除兩種狀態,所以方案總數最多就是2^48種,這樣搜尋非常盲目,顯然會超時。

解決的方法是有選擇的進行刪點。基本思路就是,一條長度不超過m的最短路徑上的點,至少有乙個點是要被刪掉的(至於刪除哪個,可以列舉嘗試),刪掉乙個點後再重新求最短路徑,如果新求出的最短路徑長度仍然不超過m,那麼就在新的路徑上再找出乙個點刪掉,然後再求最短路徑,重複以上方法直到求出的最短路徑長度超過m,那麼就找到了乙個解。但這個解未必就是最優解,所以這裡用遞迴的方法搜尋了多組解進行對比求出最優解。遞迴步驟如下:

1.尋找起點到終點的最短路徑,如果最短路徑長度超過m,則表示已經找到一種刪除方案,記錄該方案下的刪除點數目。回溯再找出下一種方案。否則進入下一步。

2.列舉最短路徑中的除1號點和n號點外的某乙個點,將其刪除。

這樣的搜尋使得列舉的點的數量得到控制。搜尋的每一層都會從n-2個點中選擇乙個進行刪除,在上述方法中,還可以用迭代加深的方法來搜尋,也就是說,先找到刪除1個點的方案,看是否存在;如果不存在,則再找到刪除2個點的方案;......

#include

using namespace std;

const int maxm=10005;//最大邊數

const int maxn=105;//最大點數

struct aaa

int s,f,next;//鄰接表的域,s表示邊的起點,f表示邊的終點,next指向點s的下一條邊

aaa c[maxm];//圖的領接表

int sta[maxn],fa[maxn],zh[maxn];//程式中使用鄰接表儲存圖中的邊,sta陣列為每個點的鄰接表表頭指標,c陣列儲存了每條邊。fa陣列和zh陣列用於廣搜最短路徑。其中zh陣列為廣搜使用的佇列,fa陣列儲存了廣搜的路徑,fa[i]表示i節點的父親。

int d[maxn][maxn],e[maxn];//d陣列儲存了當前搜尋到的最短路徑,d[i,j]表示搜尋的第i層(第i次尋找最短路徑)中尋找到的最短路中的第j個點的編號

bool b[maxn];

int n,m,now,tot;//now用於建鄰接表時的累加,tot為迭代加深搜尋的「界」

bool goal;//表示是否搜到了滿足要求的界(不刪除超過tot個點的解),如果找到了則goal=true,搜尋退出

void ins(int s,int f)//這裡記錄每條邊的起始點還有終點,並且記錄好具有相應起始點的鄰接表指標

now++;

c[now].s=s;

c[now].f=f;

c[now].next=sta[s];//一開始指向的指標為0,但是隨著同乙個起始點的多條邊的累加now,next會指向相應c陣列的下標位置

sta[s]=now;//指向該s起點的最後一條邊的相應下標

void bfs()//其實這裡個廣搜尋找最短路徑是建立在點與點之間的步數上的,就是說,如果起點到終點之間要經過兩個點,而另外一條路徑要經過三個點,那麼就說經過兩個點的比較近,也就是最短

int i,cl,op,k,t;

cl=0;op=1;

for(i=1;i<=n;i++) fa[i]=0;//清空所有父節點

zh[1]=1;//佇列的起點為第乙個點

fa[1]=-1;//預設根節點的父節點為-1

while(clcl++;k=zh[cl];//一開始zh儲存的是第乙個點,然後通過後面的遍歷該點的所有邊,並將所有起點為第乙個點的邊的終點記錄下來,所以每次進行這一步時,就是輪到另乙個點進行遍歷所有邊

for(t=sta[k];t;t=c[t].next)//由於sta陣列記錄的是具有共同起始點的邊的領接表,且數值上等於最後乙個結構邊的下標,然後這裡的t沒有寫關係式,預設就是等於0的時候跳出,然後每次迴圈後,由於c[t].next記錄的是有共同起始點的另一條結構邊的下標,所以通過這個可以將具有共同起始點的所有邊都遍歷完

if(b[c[t].f]&&fa[c[t].f]==0)//首先這裡的b陣列都是true,然後這裡fa[c[t].f]是求該邊的終點的父節點,其實這裡得到的就是該邊的起點,也就是說要保證該終點只有乙個父節點,方便後期獲得整條最短路徑,記錄了整個路徑的點。同時這裡說其為0也是為了避免兩條邊有共同的終點時對父節點造成影響,避免了這種情況的出現,使得每乙個點有且只能出現一次記錄父節點

op++.zh[op]=c[t].f;fa[c[t].f]=c[t].s;//佇列記錄終點,父節點記錄邊的起始點

if(c[t].f==n) break;//每次都要檢驗一下是否已經到達終點n,此時用的步數肯定是最少的,沒有的話就繼續迴圈,有的話就退出迴圈

if(fa[n]) break;//發現終點n已經被找到了,也就是有父節點可以到達,就再跳出最外層迴圈。

void dfs(int deep)//深搜找到一組刪除點數不超過tot的解

int i,cl,op,l,k;

if(goal) return;

bfs();//廣搜尋找最短路徑

if(fa[n]==0)//這裡是一種無可奈何的選擇,發現了其中的最短路徑就是從第乙個點到終點n,所以如果這兩點之間的距離超過m,那就成立,不用刪除任何點,但是如果兩點之間的距離小於m,那也沒辦法,還是要輸出,因為這兩點都不能刪除,也就是說一定會存在最短距離小於m的

goal=true;

return;

l=0;

for(k=n;k>1;k=fa[k])//通過這裡的迴圈,將每次得到的最短路徑的路徑節點記錄在d[maxn][maxn]中,當然,第一點沒有進行記錄

l++;d[deep][l]=k;//其實仔細一想也可以知道,這裡的l記錄的就是從起始點到終點n的行進步數

if(l>m)//如果此時行進步數大於要求的最小步數m,就直接返回,說明此時刪除的點數滿足題意

goal=true;

return;

if (deep>tot) return;//避免刪除的點數超過規定的界,也就是說規定刪除的點的個數

for (i=2;i<=l;i++)//遍歷d[maxn][maxn]中從除n外的點進行按刪除點數來進行刪除,比如第一輪做刪除乙個點時,分別嘗試去掉最短路徑中記錄的所有點中的乙個,看是否滿足題意,如果不滿足,則跳出迴圈,增加界tot,進行刪除兩個點,由**可知,在原來刪除乙個點的基礎上得到最短路徑再進行刪除第二個點,滿足題意就跳出得到結果,依次進行下去得到結果。

b[d[deep][i]]=false;//這裡就是去掉最短路徑中乙個點,將其設定為false,那麼在後期進行dfs時,由於要求b為true時才可以利用該點,現在該點不能用

if(e[d[deep][i]]==0) dfs(deep+1);//這裡檢驗e陣列是為了避免同一情況的出現,比如第乙個刪除第二個點,第二次刪除第三個點,然後情況二時第一次刪除第三個點,第二次刪除第二個點,這樣就會重複浪費時間。然後通過dfs記錄刪除這乙個點後的最短路徑

b[d[deep][i]]=true;//將該點變為可用,這樣就可以進行下乙個點的刪除實驗

e[d[deep][i]]++;//該點已經嘗試過的就要進行標記,防止重複實驗

int make()

int i,j;

goal=false;//作為乙個標誌,當goal為true時,輸出

for(i=0;i<=n;i++)//每次將「界」增加1,看是否存在解

tot=i;

for(j=1;j<=n;j++)

b[j]=true;

memset(e,0,sizeof(e));//同下所示,都是將e陣列的值初始化為0

dfs(1);//進行深搜

if(goal) return i;//如果發現goal已經標記為true,就說明此時刪除的點數i符合題意

return n;//遇到這種情況就可以開心的選擇放棄了,沒有符合的

int main()

int i,s,f,g;

while(true)

cin>>n>>g>>m;//其中n表示點數,g表示邊數,m表示從1點到n點的長度最少滿足m

if(n==0) break;

memset(sta,0,sizeof(sta));//void *memset(void *s,int ch,size_t n);將 s 中前 n 個位元組用 ch 替換並返回 s ,這裡將sta初始化為0

now=0;

for(i=1;i<=g;i++)

cin>>s>>f;//根據上邊輸入的邊數,這裡輸入每條邊相應的起點還有終點

ins(s,f);//根據輸入的起點還有終點來建立邊的鄰接表

g=make();//make函式為迭代加深的主幹,遞增搜尋的深度,一旦找到解就退出

cout<

上海市南匯汽車站 上海市莘松路春九路

上海市松江區 莘松路春九路 1.從莘松路向西南方向,前往春九路 7 公尺 2.向左轉,繼續沿莘松路前行 2.5 公里 3.向左轉,進入莘東路 220 公尺 4.在1路口向右轉,朝莘譚路行進 220 公尺 5.向左轉,進入七莘路 850 公尺 6.向右轉上匝道,前往 外環高速 15 公尺 7.在交岔路...

表的設計1

專案背景 為其他平台提供介面,根據uuid獲取集群,任務,case 自動化執行,返回執 況 問題 uuid可能對應多個集群,需要多個集群執行後才能返回,執行鏈路長,測試環境資料不全容易中斷 1 uuid 1個 多個集群cluster 1個集群cluster 1個任務task 1個任務task 多個c...

學習筆記1 無人駕駛汽車的運作方式

five core components of the self driving car 1.計算機視覺 2.感測器融合 3.定位 4.路線規劃 5.控制 計算機視覺和感測器融合 這兩大核心是為了確定車輛執行時周圍的環境,比如說當前路況怎樣,自己周圍汽車數量和與之對應的距離等等。定位 希望通過高精度...