遊戲 最短路,拆點

2022-02-18 15:26:55 字數 3523 閱讀 3524

把問題抽象成圖論應該不難(也許都不用抽象?),但是怎麼建邊怎麼跑就千差萬別了。

首先應該注意到的一點是座標的範圍是0~500,也就是501*501個位置,所以陣列/佇列不要開小。

另外題目給出的莉露露沒說位置不能重複,所以每個點可能不止入隊一次,仍然要注意陣列大小。

剛開始一直在想複雜度與n掛鉤的演算法,但是它掛了。

可以發現,n與x*y的差距並不打,所以如果正解複雜度可接受n的話那麼拿xy作為複雜度應該沒有問題。

先大概講一下考場上的暴力(80分,因為上面說的鍋,把陣列開大是88分):

考慮每個莉露露的行動:去乙個位置,撿起由岐,走幾步,扔出去。

如果我們要對於每乙個格仔進行轉移而不是針對每乙個莉露露的話,去乙個位置撿起由岐這個操作就可以變成:

找到最近的莉露露來這個位置,再讓這個莉露露去走,扔。

走和扔的話就能比較簡單的轉移了。

那麼首先的乙個問題是,怎麼找到離每乙個點最近的莉露露有多遠?

所有的二維幾何題都可以用kd-tree亂搞。貌似的確可以這也是我考場上打了一半的思路。

但是這其實就是乙個bfs,以每乙個莉露露為源點不斷擴散直至每個點都會被搜尋到一次。

複雜度o(xy)。可以考慮建乙個超級源點其距離值為-1,由它連向每乙個莉露露,當然也可以直接搞。

然後假如我們已經找到了這個陣列叫nst吧,那麼怎麼跑最短路?

我們假設由岐始終沒著地:在被扔之前另乙個莉露露已經到了落點去準備接住了。(當然不會影響答案)

那麼她就始終在莉露露的手裡了,所有的莉露露只有兩種操作。

1.走一步,費用c,那麼就想四周連邊(不用真的建邊直接for列舉就行)。連4條邊。

2.扔。目的地只能是本行或本列上的,還是列舉,連了x+y條邊。

而因為我們假設不讓由岐落地,所以在到了目的地之後還要讓最近的莉露露來接住她,費用就是ax+b+c*nst[tx][ty]

所以總邊數是x3級別的,複雜度也是。

另外還可以優化一下,如果你是被從這一列扔過來的,那麼你不會再被扔到這一列,行同理,開乙個bool陣列更新最小距離時記錄就好。

這樣的話邊數能減少一些,具體多少看測試點,反正效果還可以。

1 #include2 #include3 #include4 #include5

using

namespace

std;

6const

int xx=,yy=;

7#define tx qx[h]+xx[i]

8#define ty qy[h]+yy[i]

9struct ps};

10 priority_queueq;

11bool l[505][505],r[505][505

];12

int com,nst[505][505],x,y,n,qx[500155],qy[500155

],gx,gy,sx,sy;

13long

long a,b,c,dt[505][505

];14 signed main());

23while(!q.empty())

27#define kx x+xx[i]

28#define ky y+yy[i]

29for(int i=0;i<=3;++i)if(kx>=0&&kx<=x&&ky<=y&&ky>=0&&dt[kx][ky]>d+c)

30 l[kx][ky]=r[kx][ky]=0,q.push((ps));

31if(!l[x][y])for(int i=0;iif(dt[i][y]>d+a*(x-i)+b+c*nst[i][y])

32 l[i][y]=1,r[i][y]=0,q.push((ps));

33if(!l[x][y])for(int i=x+1;i<=x;++i)if(dt[i][y]>d+a*(i-x)+b+c*nst[i][y])

34 l[i][y]=1,r[i][y]=0,q.push((ps));

35if(!r[x][y])for(int i=0;iif(dt[x][i]>d+a*(y-i)+b+c*nst[x][i])

36 r[x][i]=1,l[x][i]=0,q.push((ps));

37if(!r[x][y])for(int i=y+1;i<=y;++i)if(dt[x][i]>d+a*(i-y)+b+c*nst[x][i])

38 r[x][i]=1,l[x][i]=0,q.push((ps));39}

40 }

view code

100%演算法就是稍微優化了一下建邊。

稍微改變狀態定義,其實每次被扔出去可以被分為兩個階段,從那個ax+b能yy一下。

首先花費b的費用起飛,然後沿著乙個方向飛,每飛乙個格仔的費用是a,著陸的費用就是最近的莉露露撿起她的費用c×nst。

當然也可以不飛,一步一步走,這裡和暴力是一樣的。

那麼因為被扔的操作每次也只會向上下左右四個格仔轉移了,所以複雜度有了保證。

然而你現在需要弄清楚她有沒有起飛有沒有著陸,如果在飛的話她是在橫著飛還是豎著。

拆點,拆成3個,分別表示0在地上,1在橫著飛,2在豎著飛。

可以從0轉移到1和2,費用為b。

可以從1,2轉移到0,費用為c×nst。

可以從0轉移到相鄰4個格仔的0,費用為c。

可以從1轉移到左右兩個格仔的1,費用為a。

可以從2轉移到上下兩個格仔的2,費用為a。

總邊數10n2。跑dijkstra,複雜度乘個log,問題不大。

據secret測試可以暴力把所有邊都建出來也能a,但是不推薦大常數者使用。

1 #include2 #include3 #include4 #include5 #include6

using

namespace

std;

7const

int xx=,yy=;

8#define tx qx[h]+xx[i]

9#define ty qy[h]+yy[i]

10struct ps};

11 priority_queueq;

12int com,x,y,n,qx[500155],qy[500155

],gx,gy,sx,sy;

13long

long a,b,c,dt[3][505][505],nst[505][505

];14 signed main());

23while(!q.empty())

27#define kx x+xx[i]

28#define ky y+yy[i]

29if(!st));

32for(int s=1;s<=2;++s)if(dt[s][x][y]>d+b)

33 q.push((ps));

34 }else

);37

if(dt[0][x][y]>d+nst[x][y])q.push((ps));38}

39}40 }

**複雜度不大,1.5k

拆點最短路 新板子get

傳送門 timelimit 1000ms memorylimit 32768mb 64 bit integer io format i64d problem description 說粗來泥萌可能不信,home w喜歡的公主被魔王抓走了!home w當然是要去救公主的,但是魔塔的結構很複雜,home...

HDU 5521 Meeting 拆點 最短路

題目鏈結 給m個由圖中結點組成的點集,點集中的點兩兩連通且距離為相等的ti。現有兩人分別從1和n點處同時出發嗎,問能否相遇以及相遇的最短時間。很容易想到直接分別以點1和點n為起始點求最短路,再遍歷各個點即可求得最短相遇時間。然而建圖上卻有問題 這個題中的邊是以點集的形式給出,極端情況下可能會出現有1...

HDU 5521 Meeting 拆點 最短路

題目鏈結 給m個由圖中結點組成的點集,點集中的點兩兩連通且距離為相等的ti。現有兩人分別從1和n點處同時出發嗎,問能否相遇以及相遇的最短時間。很容易想到直接分別以點1和點n為起始點求最短路,再遍歷各個點即可求得最短相遇時間。然而建圖上卻有問題 這個題中的邊是以點集的形式給出,極端情況下可能會出現有1...