【NOIP複習】最短路總結

【模板】html

 1 /*堆優化Dijkstra*/
 2 
 3 void dijkstra()
 4 {   
 5     priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > que;//定義大頂堆
 6     for (int i=1;i<=n;i++) vis[i]=0,dis[i]=INF;
 7     dis[1]=0;
 8     que.push(make_pair<ll,int>(0,1));
 9     while (!que.empty())
10     {
11         int head=que.top().second;que.pop();
12         vis[head]=1;//從堆中彈出時代表找到了最短路,再也不訪問
13         for (int i=0;i<E[head].size();i++)
14         {
15             edge now=E[head][i];
16             if (!vis[now.to] && dis[now.to]>dis[head]+(ll)now.len)
17             {
18                 dis[now.to]=dis[head]+(ll)now.len;
19                 que.push(make_pair<ll,int>(dis[now.to],now.to));
20             }
21         }
22     }
23     printf("%lld",dis[n]);
24 }
 1 /*SPFA*/
 2 void spfa(int start)
 3 {
 4     memset(inque,0,sizeof(inque));
 5     for (int i=1;i<=tot;i++) dis[i]=INF;
 6     dis[start]=0;
 7     inque[start]=1;
 8     que.push(start);
 9     while (!que.empty())
10     {
11         int head=que.front();que.pop();
12         inque[head]=0;
13         for (int i=0;i<E[head].size();i++)
14         {
15             int to=E[head][i].to,len=E[head][i].len;
16             if (dis[to]>dis[head]+len)
17             {
18                 dis[to]=dis[head]+len;
19                 if (!inque[to])
20                 {
21                     inque[to]=1;
22                     que.push(to);
23                 }
24             }
25         }
26     }
27 }
 1 /*dfs版本SPFA-判斷是否存在負環*/
 2 void spfa(int u)
 3 {
 4     if (vis[u])
 5     {
 6         flag=1;
 7         return;
 8     }
 9     vis[u]=1;
10     for (int i=0;i<E[u].size();i++)
11     {
12         int to=E[u][i].to,len=E[u][i].len;
13         if (dis[to]>dis[u]+len)
14         {
15             dis[to]=dis[u]+len;
16             spfa(to);
17             if (flag) return;
18         }
19     }
20     vis[u]=0;
21 }
22 
23 void solve()
24 {
25     memset(vis,0,sizeof(vis));
26     flag=0;
27     for (int i=1;i<=n;i++)
28     {
29         spfa(i);
30         if (flag) break; 
31     } 
32     puts(flag?"YES":"NO");
33 }
1 /*Floyd*/
2     for (int i=0;i<n;i++)
3         for (int j=0;j<n;j++)
4             for (int k=0;k<n;k++)
5                 f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

 

例題node

【POJ1062】-有限制條件的最短路c++

題意:每一個人都有一個物品,對應必定的錢數,想要獲得此物品能夠直接出錢,也能夠經過用其餘人的物品並添加一些錢來交換優惠購買物品。另外,每一個人都有一個等級,要求和你交易的全部人中不能有任何兩人的等級相差m以上。爲了方便起見,咱們把全部的物品從1開始進行編號,酋長的允諾也看做一個物品,而且編號老是1。每一個物品都有對應的價格P,主人的地位等級L,以及一系列的替代品Ti和該替代品所對應的"優惠"Vi。若是兩人地位等級差距超過了M,就不能"間接交易"。你必須根據這些數據來計算出探險家最少須要多少金幣才能娶到酋長的女兒。 數組

思路:因爲物品編號從1開始,咱們假定0也是一個物品做爲起點,它到其它物品的距離就是各個物品的原始價值。開始時,若是兩種物品主人的等級限制M在規定範圍之內,且j能用i替換,則將優惠價格視做從i到j的一條權值爲優惠價的路徑;若是在範圍之外,就設爲INF。處理主人地位等級限制條件的方法:咱們依次枚舉每個物品,將它的等級L做爲交易中等級最高的那一個,便可以參與交易的等級範圍爲[L-M,L],預處理時將這個範圍之外的物品強制設置爲已經訪問過,再進行Dijkstra便可。ide

 

【POJ3255&BZOJ1726】-次短路優化

[方法一]在跑Dijkstra的過程當中保留次短路。對於Dijkstra判斷中取出的每個點,若是到它的最短距離大於當前該點的次短距離,則當前該點已經取到最短距離和次短距離,不進行操做,不然進行兩次判斷:若是小於最短邊,則賦給最短變,並將最短邊賦給次短邊;或者若是大於最短變且小於次短邊,則賦給次短邊。兩次完成以後均要加入優先隊列(!)。spa

 1 /*核心代碼*/
 2         for 從當前點抵達的每個點
 3         {
 4             int d=head.len+w[k];
 5             if (dis[v[k]]>d)
 6             {
 7                 swap(dis[v[k]],d);
 8                 temp.len=dis[v[k]];temp.num=v[k];
 9                 que.push(temp);
10             }
11             if (dis[v[k]]<d && secondis[v[k]]>d)
12             {
13                 secondis[v[k]]=d;
14                 temp.len=secondis[v[k]];temp.num=v[k];
15                 que.push(temp);
16             }
17         }

[方法二]正反跑兩次SPFA。枚舉每一條邊,若是起點到一個端點的最短路+另外一個端點到終點的最短路+該邊長度 ≠ 最短路,則和答案比較更新最小值。3d

1 /*核心代碼*/
2 /*dis1爲起點到各個點的最短路,dis2爲各個點到終點的最短路(又終點開始方向跑的最短路)*/
3    for (int i=1;i<=r;i++)
4     {
5         int now=dis1[u[i]]+dis2[v[i]]+w[i];
6         if (now!=mx) ans=min(ans,now);
7         now=dis1[v[i]]+dis2[u[i]]+w[i];
8         if (now!=mx) ans=min(ans,now);
9     }

 

【POJ1511】-從源點出發返回源點的最短路code

思路:先跑一遍SPFA求出單源點最短路,再方向建圖從新跑一次最短路。二者相加獲得的和即爲答案。htm

 

【POJ1860】-判斷正環&帶操做的邊長

題意:有若干種貨幣,某些幣種之間可兌換,給出各類兌換時的匯率和手續費,任何兌換都是雙向的,可是兩個方向的匯率和手續費可能不一樣,並告知你如今擁有的貨幣種類(只擁有一種)及數量,問是否能夠經過貨幣建兌換最後回到本幣種後錢數有所增長。

思路:SPFA跑最長路。注意題目中有向圖的邊長是須要經過運算獲得的,按照通常最短路判斷寫法就能夠了。

if (did[v]<(dis[u]-手續費)*匯率)

(不知道連接裏一年前的本身在講些什麼)

 

BZOJ3436】-差分約束

題意:有n個值a1..an,獲得一些諸如:ai與aj之差不大於x,ai與aj之差不小於y的約束條件,ai與aj相等的約束條件。問是否有可能?

思路:a>=b+c → b<=a-c,由a向b連一條-c的邊;a<=b+c,由b向a連一條c的邊;a=b → b<=a<=b,互相連一條0的邊。dfs版SPFA判斷負環。

 

POJ3169】-差分約束

n頭牛從小到大排,它們之間某些距離不能大於一個值,某些距離不能小於一個值,求第一頭牛和第N頭牛之間距離的最大值。

思路:如BZOJ3436構造邊,區別爲本題限制條件不存在環狀,最後求的是dis[n]-dis[i]<=S的S值,戟dis[n]<=dis[i]+S,用最短路徑便可。

另外,若是題目要 求的是最大值,就將約束條件轉化爲">="形式,跑最長路。

 

【BZOJ4152】-建圖!

題意:給定平面上的n個點,定義(x1,y1)到(x2,y2)的費用爲min(|x1-x2|,|y1-y2|),求從1號點走到n號點的最小費用。

思路:按照某維座標排序,相鄰兩個點在這一維度上的差值最小,因此兩兩連邊,長度爲這一維度上的差值而後跑最短路便可。

 

OpenJudge3368】-限制訪問順序,多條路徑覆蓋全圖的最小值(!)

題意:諸葛亮要征服N城市。然而,City-X在擊敗City-2,City-3……City-x-1後擊敗。關羽,張飛,趙雲,每一個人都應該領導一個軍隊。三個軍隊從City-0出發,征服全部的城市並返回City-0。求三個軍隊的行程總長的最小值。

 思路:首先求出最短路徑。咱們用dp[i][j][k]表示當前三個部隊分別位於i、j、k時的答案(j<=k<=i)。分別考慮從i、j、k中的一支部隊走到i+1的狀況,總共三個遞推式。對於返回,直接假設他們都走到最終狀態後統一返回,即0到各自最終狀態的最短路徑。因爲dp[i+1]只和dp[i]有關,可使用滾動數組。

 1 /*核心代碼*/
 2 for(int i=0;i<n;++i)
 3     {
 4         cur^=1;
 5         memset(dp[cur],0x3f,sizeof(dp[cur]));//注意每次滾動數組都要清成無窮大,i=3的時候dp[0][0][0]!=0
 6         for(int j=0;j<=i;++j)
 7             for(int k=j;k<=i;++k)
 8             {
 9                 dp[cur][j][k]=min(dp[cur][j][k],dp[1-cur][j][k]+dist[i+1][i]);
10                 dp[cur][k][i]=min(dp[cur][k][i],dp[1-cur][j][k]+dist[j][i+1]);
11                 dp[cur][j][i]=min(dp[cur][j][i],dp[1-cur][j][k]+dist[k][i+1]);
12                 if (i==n-1) 
13                 {
14                     ans=min(ans,(ll)dp[cur][j][k]+(ll)dist[0][n]+(ll)dist[0][j]+(ll)dist[0][k]);
15                     ans=min(ans,(ll)dp[cur][k][i]+(ll)dist[0][n]+(ll)dist[0][k]+(ll)dist[0][i]);
16                     ans=min(ans,(ll)dp[cur][j][i]+(ll)dist[0][n]+(ll)dist[0][j]+(ll)dist[0][i]);
17                 }
18             }
19     }

 

BZOJ4093】含有中樞的最短路(!!)

題意:有鏈接N (1 < = N < = 20,000)個農場的航班。對於任何航班,指定了其中的k個農場做爲樞紐。 (1 < = K <= 200 , K < = N)。目前,共有M種單向航班( 1 < = M < = 20,000 ),第i個航班從農場u_i至農場v_i花費d_i ( 1 < = d_i < =10,000 )美圓。航班保證u_i或者v_i至少有一個是樞紐,任意兩個農場至多隻有一個航班,保證u_i≠v_i。共收到Q個度假請求,(1 < = Q < = 50,000),其中第i個請求是從農場a_i至農場b_i,問每一個請求是否知足 ,並計算能知足的度假請求的最小費用總和。

 思路:顯然,每一個非中樞的周圍一定是中樞,也就是說,中樞間能夠經過直接相連,或經過一個非中樞點鏈接。預處理 直接相連 或 間隔一個非中樞點 相連的兩個中樞的距離,用Floyd求出全部中樞之間的最短路。接下來處理出全部的中樞到全部節點之間的最短路。因爲已知兩個中樞(u,v)之間的最短路,對於從v出發抵達的下一個農場vv,很容易獲得(u,vv)。最後對於查詢的(a,b),若是a是一箇中樞,直接能夠利用上一步處理出來的信息。不然枚舉a指向的每個中樞c和b之間的最短路,再加上ac的距離,最小的那個即爲最短路。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXK=200+5;
const int MAXN=20000+50;
const ll INF=1e12;
struct node
{
    int to,dis;
};
vector<node> E[MAXN];
int n,m,k,q;
ll d[MAXK][MAXK],dis[MAXK][MAXN];
int id[MAXN],num[MAXN];

void addedge(int u,int v,int w)
{
    E[u].push_back((node){v,w}); 
}

void init()
{
    scanf("%d%d%d%d",&n,&m,&k,&q);
    memset(id,0,sizeof(id));

    for (int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
    }
    for (int i=1;i<=k;i++)
    {
        int x;
        scanf("%d",&x);
        id[x]=i,num[i]=x;
    }
}

void prep()
{        
    for (int i=1;i<=k;i++)
        for (int j=1;j<=k;j++)
            d[i][j]=INF;
    for (int i=1;i<=k;i++) d[i][i]=0;
    for (int i=1;i<=k;i++)
    {
        int u=num[i];
        for (int j=E[u].size()-1;j>=0;j--)
        {
            int v=E[u][j].to;
            if (id[v]) d[i][id[v]]=min(d[i][id[v]],(ll)E[u][j].dis);
                else
                {
                    for (int _k=E[v].size()-1;_k>=0;_k--)
                    {
                        int vto=E[v][_k].to;
                        if (id[vto] && vto!=num[i]) d[id[u]][id[vto]]=min(d[id[u]][id[vto]],(ll)E[u][j].dis+(ll)E[v][_k].dis);
                    }
                }
        }
    }    
    
    for (int _k=1;_k<=k;_k++)
        for (int i=1;i<=k;i++)
            for (int j=1;j<=k;j++)
                if (i!=j && j!=_k && _k!=i) d[i][j]=min(d[i][j],d[i][_k]+d[_k][j]); 
}

void prep2()
{
    for (int i=1;i<=k;i++)
        for (int j=1;j<=n;j++) dis[i][j]=INF;
    for (int i=1;i<=k;i++)
        for (int j=1;j<=k;j++) dis[i][num[j]]=d[i][j];
        
        
        
    for (int i=1;i<=k;i++)
    {
        for (int _k=0;_k<E[num[i]].size();_k++)//注意這裏是E[num[i]]不是num[i],檢查了40分鐘才發現QAQ
            for (int j=1;j<=k;j++)
            {
                int to=E[num[i]][_k].to;
                dis[j][to]=min(dis[j][to],d[j][i]+E[num[i]][_k].dis);
            }
    }
}

void solve()
{
    int t=0;
    ll totalans=0;
    for (int i=0;i<q;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        ll ans=INF;
        if (id[a]) ans=dis[id[a]][b];
            else
            {
                for (int j=0;j<E[a].size();j++)
                {
                    int v=E[a][j].to;
                    if (id[v]) ans=min(ans,E[a][j].dis+dis[id[v]][b]);
                }
            }
        if (ans<INF)
        {
            totalans+=ans;
            t++;
        }
    }
    printf("%d\n",t);
    printf("%d",totalans);
}

int main()
{
    init();
    prep();
    prep2();
    solve();
    return 0;
}
完整代碼

 

BZOJ1706】-恰巧通過n條邊的最短路。

思路:Floyd矩陣乘法快速冪

 1 /*核心代碼*/
 2 node operator * (node a,node b)
 3 {
 4     node c;
 5     for (int i=1;i<=cnt;i++)
 6         for (int j=1;j<=cnt;j++) c.dis[i][j]=INF;
 7     for (int k=1;k<=cnt;k++)
 8         for (int i=1;i<=cnt;i++)
 9             for (int j=1;j<=cnt;j++)
10                 c.dis[i][j]=min(c.dis[i][j],a.dis[i][k]+b.dis[k][j]);
11     return c;
12 }
13 
14 void solve()
15 {
16     int k=n-1;
17     result=f,origin=f;//f爲初始的鄰接矩陣
18     while (k)
19     {
20         if (k&1) result=result*origin;
21         k>>=1;
22         origin=origin*origin;
23     }
24     printf("%lld",result.dis[id[s]][id[e]]); 
25 }

 


 

估計NOIP不會考的平面圖轉最小割

【BZOJ1001狼抓兔子】

【BZOJ2007海拔】

相關文章
相關標籤/搜索