Hile每日算法-3.29-分層建圖

分層建圖

首先來一道題,題意是這樣的:

給定一張有向圖(遊戲地圖),一對起點和終點,每個點代表一個城市,你從起點開車到終點,每次在兩個城市間移動需要1h。每個點(城市)都有五種可能的情況:

1.該點沒有任何道具;

2.該點有阻礙物需要停止1h;

3.到達該點時遊戲失敗(保證起點和終點不爲3);

4.該點有氮氣,接下來連續兩次移動速度加倍(倍數不可疊加,次數可以);

5.該點有沙子,接下來連續兩次移動速度減半(同上);

求從起點到終點最快用時,如果不能到達終點輸出-1.

分析題目,單源最短路徑,很顯然就該用 SPFA dijkstra算法。對於各類點,遇到1不管,2的話將該點的出邊權值(用時)加一,3的話刪除連接這個點的所有邊。

但是,4和5兩種情況怎麼處理?怎麼實現同一條邊權值(用時)加倍和減半的操作,怎麼讓1、0.5、2三種邊權同時存在於一條邊上呢?

換個想法,與其糾結在一條邊上進行的多次修改,不如把所有可能的權值都建立一條邊,也就是說,分層建圖

(當然,這並不是什麼算法,只是一種建圖的技巧/思想。

就這題而言,可以將圖分爲三層,分別叫做G1G2G3,G1的邊權都爲1,G2都爲0.5,G3都爲2,初始在G1層的起點。對於每一個第四類點(氮氣),我們可以向G3的同一點連一條無權邊(權值爲0),然後對於所有和該點距離在2以內的G3層點向G1連一條無權邊,如圖所示其中G1.1爲氮氣:

(今天剛學Graphiviz,畫的太醜,暫時湊合着看吧)

同理,遇到第五類點作相同處理,這題就變成裸的dijkstra啦。

下面來做道題目體會一下:

bzoj2763: [JLOI2011]飛行路線

給出nn個點,mm條邊的無向圖,一對起點和終點,給你k (k10)k\ (k\le10)次將當前邊權變爲00的機會,求從起點到終點的最小代價。

由於kk比較小,我們可以用分層建圖的思想建出k+1k+1層圖,分別表示在各點已經使用了0,1...k0,1...k次機會的狀態,各層之間轉移的邊權爲00,建完之後跑dij即可。

AC代碼:

#include <bits/stdc++.h>
#define PII pair<int,int>
#define N 10010
#define ll long long
using namespace std;
bool vis[N*11];//用0~n-1,n~2n-1...kn~(k+1)n-1表示k+1層的點
vector<PII> G[N];//由於各層的連通情況都相同,只需對初始層建圖,各層用對n取模訪問
int n,m,k,s,t,d[N*11];
void dijkstra(int s)
{
    priority_queue<PII,vector<PII>,greater<PII> > pq;
    pq.push(make_pair(0,s));
    d[s]=0;
    while(!pq.empty())
    {
        int u=pq.top().second;pq.pop();
        if(vis[u])continue;vis[u]=1;
        for(int it=0;it<G[u%n].size();it++)//QAQ,bzoj不能for(auto:G)
        {
            PII i=G[u%n][it];
            int v=i.second+u-u%n;
            if(d[u]+i.first<d[v])//正常鬆弛
            {
                d[v]=d[u]+i.first;
                pq.push(make_pair(d[v],v));
            }
            if(v+n<(k+1)*n&&d[v+n]>d[u])//到下一層
            {
                d[v+n]=d[u];
                pq.push(make_pair(d[v+n],v+n));
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        G[u].push_back(make_pair(w,v));
        G[v].push_back(make_pair(w,u));
    }
    memset(d,0x3f,sizeof(d));
    dijkstra(s);
    printf("%d",d[k*n+t]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章