国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

第三章 圖論 No.1單源最短路及其綜合應用

這篇具有很好參考價值的文章主要介紹了第三章 圖論 No.1單源最短路及其綜合應用。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

做乘法的最短路時,若權(quán)值>=0,只能用spfa來做,相等于加法中的負權(quán)邊

1129. 熱浪

1129. 熱浪 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

單源最短路,稀疏圖,用堆優(yōu)化Dijkstra即可,就是板子套了個背景

// 稀疏圖,用堆優(yōu)化Dijkstra存儲
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 2510, M = 2 * 6210;
typedef pair<int, int> PII;
priority_queue<PII, vector<PII>, greater<PII>> q;
int h[N], e[M], ne[M], w[M], idx = 1;
int n, m, start, ed;
int dis[N];
bool st[N];

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[start] = 0;
    q.push({0, start});
    while (q.size())
    {
        auto t = q.top(); q.pop();
        int x = t.second, d = t.first;
        if (st[x]) continue;
        st[x] = true;
        
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[y] > d + w[i])
            {
                dis[y] = d + w[i];
                q.push({dis[y], y});
            }
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d%d%d", &n, &m, &start, &ed);
    int x, y, d;
    for (int i = 1; i <= m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d), add(y, x, d);
    }
    
    dijkstra();
    printf("%d\n", dis[ed]);
    return 0;
}

debug:由于是無向圖,邊的數(shù)量要開兩倍。但是w[N]沒改,debug了很久
所以e[M], ne[M], w[M],只有h[N],其他的數(shù)組存儲的都是邊


1128. 信使

1128. 信使 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

單源最短路,稀疏圖,用堆優(yōu)化Dijkstra即可,最后統(tǒng)計所有dis的最大值并返回即可

// 單源最短路,最后將第1個點到其他點的距離累加即可
// 稀疏圖,依然是堆優(yōu)化的dijkstra
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

typedef pair<int, int> PII;
const int N = 110, M = 410;
int h[N], e[M], ne[M], w[M], idx = 1;
priority_queue<PII, vector<PII>, greater<PII>> q;
int n, m;
bool st[N]; int dis[N];

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    q.push({0, 1});
    while (q.size())
    {
        auto t = q.top(); q.pop();
        int x = t.second, d = t.first;
        
        if (st[x]) continue;
        st[x] = true;
        for (int i = h[x]; i != -1; i = ne[i]) 
        {
            int y = e[i];
            if (dis[y] > d + w[i])
            {
                dis[y] = d + w[i];
                q.push({dis[y], y});
            }
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    int x, y, d;
    for (int i = 1; i <= m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d), add(y, x, d);
    }
    
    dijkstra();
    
    int res = 0;
    bool flag = true;
    for (int i = 2; i <= n; ++ i )
    {
        if (dis[i] == 0x3f3f3f3f) 
        {
            flag = false;
            break;
        }
        res = max(res, dis[i]);
    }
    if (flag) printf("%d\n", res);
    else puts("-1");

    return 0;
}

debug:%d打成了5d,樂,竟然能編譯通過
判斷某個點的最短距離是否已經(jīng)確定,打成了if(dis[x]); dis[x] = true,這也debug了半天


1127. 香甜的黃油

1127. 香甜的黃油 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

// 多源匯最短路,由于flody可能超時,所以用單源最短路求解
// 這里用spfa
#include <iostream>
#include <cstring>
using namespace std;

const int N = 810, M = 3000;
int h[N], e[M], ne[M], w[M], idx = 1;
int dis[N]; bool st[N];
int s, n, m;
int q[N];
int id[N];

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void spfa(int k)
{
    memset(dis, 0x3f, sizeof(dis));
    int hh = 0, tt = 1;
    dis[k] = 0;
    q[tt ++ ] = k, st[k] = true;
    while (tt != hh)
    {
        int x = q[hh ++ ];
        if (hh == N) hh = 0;
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[y] > dis[x] + w[i])
            {
                dis[y] = dis[x] + w[i];
                if (!st[y]) 
                {
                    q[tt ++ ] = y, st[y] = true;
                    if (tt == N) tt = 0;
                }
            }
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d%d", &s, &n, &m);
    for (int i = 1; i <= s; ++ i ) scanf("%d", &id[i]);
    
    int x, y, d;
    for (int i = 1; i <= m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d), add(y, x, d);
    }
    
    int res = 0x3f3f3f3f;
    for (int i = 1; i <= n; ++ i )
    {
        spfa(i);
        int sum = 0; bool flag = true;
        for (int j = 1; j <= s; ++ j ) 
        {
            if (dis[id[j]] == 0x3f3f3f3f)
            {
                flag = false;
                break;
            }
            sum += dis[id[j]];
        }
        if (flag) res = min(res, sum);
    }
    
    printf("%d\n", res);
    return 0;
}

用自己寫的循環(huán)隊列代替queue,時間大概快了一倍
需要注意的是,循環(huán)隊列的hh和tt從0開始使用,并且都是后置++,++后還要維護循環(huán)性質(zhì)

以下是用queue實現(xiàn)的版本

// 多源匯最短路,由于flody可能超時,所以用單源最短路求解
// 這里用spfa
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

typedef long long LL;
const int N = 810, M = 1500 * 2;
int h[N], e[M], ne[M], w[M], idx = 1;
int dis[N]; bool st[N];
int s, n, m;
queue<int> q;
int id[N];

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void spfa(int k)
{
    memset(dis, 0x3f, sizeof(dis));
    dis[k] = 0;
    q.push(k), st[k] = true;
    while (q.size())
    {
        int x = q.front(); q.pop();
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[y] > dis[x] + w[i])
            {
                dis[y] = dis[x] + w[i];
                if (!st[y]) q.push(y), st[y] = true;
            }
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d%d", &s, &n, &m);
    int t;
    for (int i = 1; i <= s; ++ i ) scanf("%d", &id[i]);
    
    int x, y, d;
    for (int i = 1; i <= m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d), add(y, x, d);
    }
    
    LL res = 1e18;
    for (int i = 1; i <= n; ++ i )
    {
        spfa(i);
        LL sum = 0; bool flag = true;
        for (int i = 1; i <= s; ++ i )
        {
            if (dis[id[i]] == 0x3f3f3f3f) 
            {
                flag = false;
                break;
            }
            sum += dis[id[i]];
        }
        if (flag) res = min(res, sum);
    }
    
    printf("%lld\n", res);
    return 0;
}

1126. 最小花費

1126. 最小花費 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

轉(zhuǎn)賬金額A,當手續(xù)費的比例為z時,只能轉(zhuǎn)賬A(1 - z)
若每條邊的權(quán)值為實際轉(zhuǎn)賬的金額比例,那么從起點到終點,這個比例越大,花費的初始金額就越少,所以這題需要找一條從起點到終點比例最大的路徑
即從起點到終點經(jīng)過的所有邊的比例相乘最大,由于比例的大小為 0 < z < = 1 0 < z <= 1 0<z<=1,所以這里初始化dis數(shù)組以及鄰接矩陣為全0
接著用spfa或者dijkstra求“最短路”即可

// 同樣的單源最短路,spfa解決

#include <iostream>
using namespace std;

const int N = 2010;
double g[N][N];
int n, m, start, ed;
double dis[N]; bool st[N];
int q[N], tt, hh;

void spfa()
{
    dis[start] = 1.0;
    q[tt ++ ] = start, st[start] = true;
    while (tt != hh)
    {
        int x = q[hh ++ ];
        if (hh == N) hh = 0;
        st[x] = false;
        for (int j = 1; j <= n; ++ j )
        {
            if (dis[j] < dis[x] * g[x][j])
            {
                dis[j] = dis[x] * g[x][j];
                if (!st[j]) 
                {
                    q[tt ++ ] = j, st[j] = true;
                    if (tt == N) tt = 0;
                }
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    int x, y, z;
    for (int i = 1; i <= m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &z);
        g[x][y] = g[y][x] = 1 - (z / 100.0);
    }
    scanf("%d%d", &start, &ed);
    
    spfa();
    
    printf("%.8lf\n", 100 / dis[ed]);
    return 0;
}

920. 最優(yōu)乘車

920. 最優(yōu)乘車 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

沒之前的題目裸,需要一些思考
雖然題目沒有給定邊的權(quán)重,但是給定了公交路線,可知公交路線 a a a中, a i a_i ai? a j a_j aj?的權(quán)重為1,表示從i地到j地需要的乘車次數(shù),當如 a i a_i ai? a a a中的下標要出現(xiàn)在 a j a_j aj?之前,這是一張有向圖
所以,根據(jù)題目給定的公交路線,找出圖中所有的有向邊,最后用單源最短路就行了
由于邊的權(quán)值為1,甚至用bfs也行

// 一條路線之間的點可達,存在有向邊,線路中有n個點,存在Cn2條邊
// 邊的權(quán)值為1,表示需要乘車的次數(shù),由于權(quán)值為1,所以可以用bfs
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
using namespace std;

const int N = 510, M = 11;;
int a[N], g[N][N], q[N];
int dis[N];
int n, m, tt, hh;

int bfs()
{
    dis[1] = 0;
    q[tt ++ ] = 1;
    while (tt != hh)
    {
        int x = q[hh ++ ];
        if (hh == N) hh = 0;
        
        for (int y = 1; y <= n; ++ y )
        {
            if (y != x && !dis[y] && g[x][y]) 
            {
                dis[y] = dis[x] + 1;
                if (y == n) return dis[y];
                q[tt ++ ] = y;
            }
        }
    }
    return 0;
}

int main()
{
    scanf("%d%d", &m, &n);
    string line;
    getline(cin, line);
    for (int i = 0; i < m; ++ i )
    {
        getline(cin, line);
        stringstream s(line);
        int idx = 0, p;
        while (s >> p) a[idx ++ ] = p;
        
        for (int j = 0; j < idx; ++ j )
            for (int k = j + 1; k < idx; ++ k )
                g[a[j]][a[k]] = 1;
    }
    
    int t = bfs();
    if (t == 0) puts("NO");
    else printf("%d\n", t - 1);
    
    return 0;
}

903. 昂貴的聘禮

903. 昂貴的聘禮 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

以下是我的思路,我只是記錄一下不建議看
要獲得某個物品,有兩種方式:1. 直接購買 2. 在擁有替代品的前提下,以優(yōu)惠價格購買
此外無法找出其他方法,需要考慮的是:以優(yōu)惠價格購買時,擁有替代品的代價是什么?
若從正面考慮,假設酋長的10000金幣是一個物品,現(xiàn)可以直接購買或者使用替代品。若使用替代品以更低的價格購買,那么購買替代品的價格是多少?同樣,購買替代品也有兩種方式,是直接購買省錢還是用替代品的替代品購買省錢。這是一個無止盡的過程,什么時候會停止?物品沒有替代品時停止,這就意味著我們要遞歸到最底部后,在向上回溯的過程中,才能計算兩種方式中哪種最省錢
這是直接的思路,可能能做出來吧,沒試過


正解:
將酋長的10000金幣看成物品,并且是有向圖的終點。物品之間有什么關(guān)系?從一個點走到另一個點,前提是當前點是下一點的替代品,那么從當前點到下一點的代價就是用當前物品購買下一物品的優(yōu)惠價格
所以邊的權(quán)重為優(yōu)惠價格,當前除了以優(yōu)惠價格購買,還能直接購買。直接購買時,當前點的含義不再是一個物品,所以這里的點是一個虛擬點,到其他點的權(quán)重為直接購買的價格

若當前已經(jīng)購買了物品,走向下一物品時,直接購買的價格肯定比使用替代品高,所以不用設置已經(jīng)購買物品時,再直接購買其他物品的邊。這樣的邊沒有意義
由于存在一些物品無法使用替代品購買,所以只要設置手上沒有物品時,直接購買物品的邊
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法
虛擬點為上圖的s
起點為s,終點為1號物品(酋長的10000金幣),跑一個最短路即可
為什么起點是s?存在一些只能直接購買的物品

最后考慮等級限制,等級差距不超過m的人可以交換物品,由于題目數(shù)據(jù)量小,可以直接暴力枚舉。假設酋長的等級為a,那么需要枚舉的區(qū)間為 [ a ? m , a + m ] [a-m, a+m] [a?m,a+m],枚舉這個區(qū)間中長度為m的區(qū)間,每次枚舉跑一次最短路即可

#include <iostream>
#include <cstring>
using namespace std;

const int N = 110;
int dis[N], g[N][N];
int level[N];
bool st[N];
int n, m;

int dijkstra(int l, int r)
{
    memset(dis, 0x3f, sizeof(dis));
    memset(st, false, sizeof(st));
    dis[0] = 0;
    for (int i = 0; i <= n; ++ i )
    {
        int t = -1;
        for (int j = 0; j <= n; ++ j)
            if (!st[j] && (t == -1 || dis[j] < dis[t])) t = j;
        st[t] = true;
        for (int j = 1; j <= n; ++ j )
            if (l <= level[j] && level[j] <= r)
                dis[j] = min(dis[j], dis[t] + g[t][j]);
    }
    return dis[1];
}

int main()
{
    memset(g, 0x3f, sizeof(g));
    scanf("%d%d", &m, &n);
    int p, x, t, v;
    for (int i = 1; i <= n; ++ i ) g[i][i] = 0;
    for (int i = 1; i <= n; ++ i )
    {
        scanf("%d%d%d", &p, &level[i] , &x);
        g[0][i] = min(g[0][i], p);
        while ( x -- )
        {
            scanf("%d%d", &t, &v);
            g[t][i] = min(g[t][i], v);
        }
    }
    
    int res = 0x3f3f3f3f;
    for (int i = level[1] - m; i <= level[1]; ++ i )
    {
        int t = dijkstra(i, i + m);
        res = min(t, res);
    }
    printf("%d\n", res);
    return 0;
}

1135. 新年好

1135. 新年好 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

先用堆優(yōu)dijkstra跑單源最短路,一共是6個源點,分別是家所在的點以及親戚所在的點,保存最短距離
然后dfs爆搜,以1號點為起點,剩下五個點的全排列為搜索順序,記錄全排列中路徑的最短距離

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 5e5 + 10, M = 2e5 + 10;
const int INF = 1e9;
int h[N], e[M], ne[M], w[M], idx = 1;
int dis[6][N]; bool st[N];
int tar[10];
int n, m;
typedef pair<int, int> PII;
priority_queue<PII, vector<PII>, greater<PII>> q;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;    
}

void dijkstra(int start, int si) // si為start在dis數(shù)組中的一維下標
{
    memset(st, false, sizeof(st));
    dis[si][start] = 0;
    q.push({ 0, start });
    while (q.size())
    {
        auto t = q.top(); q.pop();
        int x = t.second, d = t.first;
        if (st[x]) continue;
        st[x] = true;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[si][y] > d + w[i])
            {
                dis[si][y] = d + w[i];
                q.push({ dis[si][y], y});
            }
        }
    }
}

int dfs(int cnt, int si, int d) // cnt為已經(jīng)拜訪的親戚數(shù)量,si為當前所在點在dis數(shù)組中的一維下標,d為遞達當前點的“距離”
{
    if (cnt == 5)  return d;
    int res = INF;
    for (int i = 1; i <= 5; ++ i )
    {
        if (!st[i])
        {
            int y = tar[i]; // 要訪問的下一個點在圖中的編號
            st[i] = true;
            res = min(res, dfs(cnt + 1, i, d + dis[si][y]));
            st[i] = false;
        }
    }
    return res;
}

int main()
{
    memset(h, -1, sizeof(h));
    memset(dis, 0x3f, sizeof(dis));
    scanf("%d%d", &n, &m);
    for ( int i = 1; i <= 5; ++ i ) scanf("%d", &tar[i]);
    int x, y, t;
    while ( m -- )
    {
        scanf("%d%d%d", &x, &y, &t);
        add(x, y, t), add(y, x, t);
    }
    
    dijkstra(1, 0);
    for (int i = 1; i <= 5; ++ i ) dijkstra(tar[i], i);
    
    memset(st, false, sizeof(st));
    printf("%d\n", dfs(0, 0, 0));
    return 0;
}

debug:雙向邊,M又少開一倍,但是TLE,以后M都開兩倍吧,這太難調(diào)試了
dfs最后要返回res,因為min要使用dfs的返回值


340. 通信線路

340. 通信線路 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

分析題意:對于每條1~n的路徑,我們只要支付第k+1大的邊的金額即可。若邊的數(shù)量少于k+1,那么支付金額為0
所以本題的最短路,短在第k+1大的邊權(quán)中,最小的那個。所有最大中找最小,自然地想到二分。二分邊權(quán),得到ans,使得ans是所有1~n路徑中,第k+1大的權(quán)值中最小的那個

二分的范圍: [ 0 , 1 e 6 + 1 ] [0, 1e6+1] [0,1e6+1],權(quán)值最小值-1~權(quán)值最大值+1
check:分析ans的性質(zhì),所有1~n的路徑中,第k+1大的邊權(quán)中最小值為ans
說明至少存在一條路徑,經(jīng)過了k條邊權(quán)大于ans的邊,即and是這條路徑中第k+1大的邊
那么其他路徑就一定經(jīng)過了多于k條權(quán)值大于ans的邊
由此得到check的檢查邏輯:根據(jù)參數(shù)x,判斷1~n的所有路徑中,是否存在一條路徑,經(jīng)過了小于等于k條的權(quán)值大于x的邊。若存在返回true,否則返回false
試想一下,x越大,check的性質(zhì)越容易滿足,x越小,性質(zhì)越難滿足

兩種特殊情況:ans為0,說明1~n的所有路徑中,至少存在一條路徑,經(jīng)過了k條權(quán)值大于0的邊,那么第k+1條邊的權(quán)值為0,需要支付金額為0
ans為 1 e 6 + 1 1e6+1 1e6+1,說明1n的所有路徑中,至少存在一條路徑,經(jīng)過了k條權(quán)值大于$1e6+1$的邊,由于邊權(quán)最大為$1e6$,所以這樣的路徑是不存在的,即1n之間不連通

如何得知1~n的所有路徑中,經(jīng)過了幾條權(quán)值大于x的邊?
對于權(quán)值大于x的邊,將其權(quán)值設置為1,否則設置為0,跑一遍最短路,就能知道1~n的最短路徑經(jīng)過了幾條權(quán)值大于x的邊
對于權(quán)值只有0和1的最短路問題,可以用雙端隊列bfs

#include <iostream>
#include <cstring>
#include <deque>
using namespace std;

const int N = 1010, M = 20010;
int n, p, k;
int h[N], e[M], ne[M], w[M], idx = 1;
int dis[N]; bool st[N];
deque<int> q;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ; 
}

bool check(int bound)
{
    memset(dis, 0x3f, sizeof(dis));
    memset(st, false, sizeof(st));
    dis[1] = 0;
    q.push_back(1);
    while (q.size())
    {
        int x = q.front(); q.pop_front();
        if (st[x]) continue;
        st[x] = true;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i], d = w[i] > bound;
            if (dis[y] > dis[x] + d)
            {
                dis[y] = dis[x] + d;
                if (d) q.push_back(y);
                else q.push_front(y);
            }
        }
    }
    return dis[n] <= k;
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d%d", &n, &p, &k);
    int x, y, d;
    while ( p -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d), add(y, x, d);
    }
    
    int l = 0, r = 1e6 + 1;
    while ( l < r )
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    
    if (l == 1e6 + 1) puts("-1");
    else printf("%d\n", l);
    return 0;
}

342. 道路與航線

342. 道路與航線 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

直接用spfa會被卡兩個數(shù)據(jù)
根據(jù)題意:道路,雙向邊,存在環(huán),正權(quán);航線:單向邊,無環(huán),負權(quán)
其中關(guān)于是否有環(huán)以及權(quán)值的正負是選擇算法的關(guān)鍵
若圖中沒有負權(quán)邊,直接用堆d
若圖中無環(huán),直接按照拓撲序遍歷所有點就能確定最短路

結(jié)合以上兩者,可以得到以下算法:
將所有點根據(jù)道路的連通性分成多個連通塊,每個連通塊之間不連通
連通每個連通塊的是航線,單向邊。讀取所有的航線后,按照拓撲序遍歷每個連通塊,對于每個連通塊,跑堆d求最短路

用id數(shù)組表示每個點所屬的連通塊編號,用二維數(shù)組blocks存儲每個連通塊中點的編號
讀入所有道路后,dfs可以遍歷連通塊中的所有點,維護出id和blocks數(shù)組信息
讀入所有航線,維護出in數(shù)組,計算所有點的入度后按照拓撲序跑堆d

// 道路是雙向的,航線單向,可能是負數(shù)也可能是正數(shù)
// 并且圖中保證不會存在環(huán)路
// 直接spfa?
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;

typedef pair<int, int> PII;
const int N = 3e4, M = 2e5;
int h[N], e[M], ne[M], w[M], idx = 1;
int n, p, r, s;
int dis[N]; bool st[N];
int id[N], cnt, in[N];
vector<int> blocks[N];
queue<int> q;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void dfs(int x)
{
    id[x] = cnt;
    blocks[cnt].push_back(x);
    for (int i = h[x]; i != -1; i = ne[i])
    {
        int y = e[i];
        if (!id[y]) dfs(y);
    }
}

void dijkstra(int k)
{
    priority_queue<PII, vector<PII>, greater<PII>> hp;
    for (auto x : blocks[k]) hp.push({ dis[x], x });
    
    while (hp.size())
    {
        auto t = hp.top(); hp.pop();
        int x = t.second, d = t.first;
        if (st[x]) continue;
        st[x] = true;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (id[x] != id[y] && -- in[id[y]] == 0) q.push(id[y]);
            if (dis[y] > d + w[i])
            {
                dis[y] = d + w[i];
                if (id[x] == id[y]) hp.push( {dis[y], y });
            }
        }
    }
}

void topsort()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[s] = 0;
    for (int i = 1; i <= cnt; ++ i )
        if (!in[i]) q.push(i);
    
    while (q.size())
    {
        int x = q.front(); q.pop(); // 連通塊id
        dijkstra(x);
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d%d%d", &n, &r, &p, &s);
    int x, y, d;
    for (int i = 0; i < r; ++ i )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d), add(y, x, d);
    }
    
    for (int i = 1; i <= n; ++ i )
    {
        if (!id[i]) 
        {
            cnt ++ ;
            dfs(i); // 維護出i所在的連通塊
        }
    }
    
    for (int i = 0; i < p; ++ i )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d);
        in[id[y]] ++ ;
    }
    
    topsort();
    
    for (int i = 1; i <= n; ++ i ) 
    {
        if (dis[i] >= 0x3f3f3f3f / 2) puts("NO PATH");
        else printf("%d\n", dis[i]);
    }
    return 0;
}

341. 最優(yōu)貿(mào)易

341. 最優(yōu)貿(mào)易 - AcWing題庫
第三章 圖論 No.1單源最短路及其綜合應用,AcWing算法提高課 課程記錄,圖論,算法

狀態(tài)表示:
集合:所有從1~n的路徑,用分界點將這些路徑一分為二
屬性:在分界點之前(包括分界點)買入 - 在分界點(包括分界點)之后能賣出的最大價值,即最大賣出價值 - 最小買入價值
f ( i ) f(i) f(i)表示以i號點為分界點的1~n路徑中,可以賺取的最大價值

狀態(tài)劃分:分界點從1~n,此時劃分的子集不遺漏地組成了整個集合(雖然有重復,但是題目要求最優(yōu)解,重復的情況不影響最優(yōu)解)

如何求路徑中的前半段最小值 d m i n ( k ) dmin(k) dmin(k)?
若與k直接相連的點有t個,那么 d m i n ( k ) = m i n ( d m i n ( s 1 ) , d m i n ( s 2 ) , . . . d m i n ( s t ) , w k ) dmin(k) = min(dmin(s_1), dmin(s_2), ... dmin(s_t), w_k) dmin(k)=min(dmin(s1?),dmin(s2?),...dmin(st?),wk?)
走到k的最小值就等于從所有走到與k直接相連的點的最小值以及自己的權(quán)值中的最小值
由于這些狀態(tài)依賴可能存在環(huán),即當dp推導出的狀態(tài)不是最優(yōu)解,所以這里用最短路求這些狀態(tài)
最短路用dijkstra或者spfa,是否能用dijkstra?由于在最短路問題中,當前點的最短路徑由與之相連的所有點的最短路徑累加上自己的權(quán)值確定,并且權(quán)值只能是正的
根據(jù)這個性質(zhì),已經(jīng)確定最短路的點在之后的更新中不可能出現(xiàn)更短的路徑

雖然這題的邊權(quán)為正,但是這題的狀態(tài)不是由之前的狀態(tài)累加,而是從之前的狀態(tài)中取min或者max,這就導致dijkstra每次的更新無法求得最優(yōu)解,需要后續(xù)的再次更新,此時的時間復雜度無法保證,這是最關(guān)鍵的,所以不使用dijkstra

那spfa呢?spfa是bellman-ford的優(yōu)化,bellman根據(jù)邊的數(shù)量進行更新,每次更新所有邊
所以bellman能夠保證最短路的邊數(shù),即時間復雜度是確定的,所以可以使用sfpa

用sfpa跑出dmin和dmax數(shù)組的信息,分別存儲了從1到k的最小值,以及從k到n的最大值
由于從k到n的起點是變化的,但是終點不變,所以這里可以建一張反向圖,以n為起點,更新dmax文章來源地址http://www.zghlxwxcb.cn/news/detail-625960.html

#include <iostream>
#include <cstring>
using namespace std;

const int N = 1e5 + 10, M = 2e6;
int w[N];
int hs[N], ht[N], e[M], ne[M], idx;
int dmin[N], dmax[N]; bool st[N];
int n, m;
int q[N];

void spfa(int h[], int dis[], int t)
{
    int tt = 0, hh = 0;
    if (t == 1)
    {
        memset(dis, 0x3f, sizeof(dmin));
        q[tt ++ ] = 1; st[1] = true;
        dis[1] = w[1];
    }
    else
    {
        memset(dis, -0x3f, sizeof(dmax));
        q[tt ++ ] = n; st[n] = true;
        dis[n] = w[n];
    }
    
    while (tt != hh)
    {
        int x = q[hh ++ ];
        if (hh == N) hh = 0;

        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if ((t == 1 && dis[y] > min(dis[x], w[y])) || (t == 2 && dis[y] < max(dis[x], w[y])))
            {
                if (t == 1) dis[y] = min(dis[x], w[y]);
                else dis[y] = max(dis[x], w[y]);
                if (!st[y]) 
                {
                    st[y] = true, q[tt ++ ] = y;
                    if (tt == N) tt = 0;
                }
            }
        }
    }
}

void add(int h[], int x, int y)
{
    e[idx] = y, ne[idx] = h[x], h[x] = idx ++ ;
}

int main()
{
    memset(hs, -1, sizeof(hs));
    memset(ht, -1, sizeof(ht));
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++ i ) scanf("%d", &w[i]);
    int x, y, t;
    while ( m -- )
    {
        scanf("%d%d%d", &x, &y, &t);
        add(hs, x, y), add(ht, y, x);
        if (t == 2) add(hs, y, x), add(ht, x, y);

    }
    
    spfa(hs, dmin, 1);
    spfa(ht, dmax, 2);
    
    int res = 0;
    for (int i = 1; i <= n; ++ i )
    {
        res = max(res, dmax[i] - dmin[i]);
    }
    
    printf("%d\n", res);
    
    return 0;
}

到了這里,關(guān)于第三章 圖論 No.1單源最短路及其綜合應用的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • 【圖論】單源最短路

    【圖論】單源最短路

    算法提高課筆記 最短路問題可以分為以下兩類: 邊權(quán)非負——樸素Dijkstra、堆優(yōu)化Dijkstra 有負權(quán)邊——Bellman-Ford、SPFA 熱浪 原題鏈接 德克薩斯純樸的民眾們這個夏天正在遭受巨大的熱浪!??! 他們的德克薩斯長角牛吃起來不錯,可是它們并不是很擅長生產(chǎn)富含奶油的乳制品

    2024年02月14日
    瀏覽(21)
  • 【圖論】單源最短路問題

    Dijkstra算法是一種單源最短路徑算法,用于找出圖中從一個源點到其他所有點的最短路徑。該算法的原理是采用貪心策略,每次將距離源點最近的點加入到已確定最短路徑的集合中,并更新其它節(jié)點的距離。具體實現(xiàn)過程如下: 初始化距離數(shù)組dist[],源點距離為0,其余點距離

    2024年02月13日
    瀏覽(19)
  • 【圖論 單源最短路】100276. 最短路徑中的邊

    【圖論 單源最短路】100276. 最短路徑中的邊

    單源最短路 圖論知識匯總 給你一個 n 個節(jié)點的無向帶權(quán)圖,節(jié)點編號為 0 到 n - 1 。圖中總共有 m 條邊,用二維數(shù)組 edges 表示,其中 edges[i] = [ai, bi, wi] 表示節(jié)點 ai 和 bi 之間有一條邊權(quán)為 wi 的邊。 對于節(jié)點 0 為出發(fā)點,節(jié)點 n - 1 為結(jié)束點的所有最短路,你需要返回一個長度

    2024年04月22日
    瀏覽(24)
  • 第三章 圖論 No.13拓撲排序

    第三章 圖論 No.13拓撲排序

    拓撲序和DAG有向無環(huán)圖聯(lián)系在一起,通常用于最短/長路的線性求解 裸題:1191. 家譜樹 1191. 家譜樹 - AcWing題庫 差分約束+拓撲排序:1192. 獎金 1192. 獎金 - AcWing題庫 由于圖中所有邊權(quán)都是正數(shù),可以直接使用topsort求解差分約束問題 根據(jù)題意,要求一個最小值,使用最長路求解

    2024年02月12日
    瀏覽(18)
  • 算法提高-圖論-單源最短路的擴展應用

    多源點單終點最短路建圖: 創(chuàng)建虛擬源點(創(chuàng)建虛擬源點的時候以是spfa為例 可以在建圖的時候建出來,也可以在spfa這直接入隊,也是虛擬源點的意思) 反向建圖變成單源點多終點,然后遍歷終點的dist即可找出最短路 這題挺簡單的就不詳細說了,主要是第一次遇到計數(shù)問題

    2024年02月16日
    瀏覽(28)
  • 算法提高-圖論-單源最短路的綜合應用

    算法提高-圖論-單源最短路的綜合應用

    多次dijkstra求每個點到其它點的最短距離, 此時相當于建好了一張圖,每個點之間的最短距離都知道了,接下來dfs搜一下怎么走最短即可 一篇博客解釋了為什么一個正向建圖求最小值,反向建圖求最大值 根本思想是保證1到n的買賣是連通的

    2024年02月11日
    瀏覽(33)
  • 算法提高-圖論-單源最短路的建圖方式

    算法提高-圖論-單源最短路的建圖方式

    建圖 找出一個牧場,它到其他牧場的距離之和最小 我是這么理解的,djsktra是一個貪心的思想,加法里面不能加負數(shù)我就不說了 求乘法最大值的時候為什么邊權(quán)必須0-1,因為在乘法最大值里面有一個邊權(quán)大于1的話那不就等價于求加法最小值的時候有一個邊權(quán)為負數(shù)的么,d

    2024年02月08日
    瀏覽(29)
  • 第三章 圖論 No.12歐拉回路與歐拉路徑

    第三章 圖論 No.12歐拉回路與歐拉路徑

    小學一筆畫問題,每條邊只經(jīng)過一次 判斷圖是否存在歐拉回路:判斷圖是否連通(存在孤立邊),再根據(jù)有向/無向具體判斷 對于無向圖來說, 歐拉路徑 中,起點和終點的度數(shù)為奇數(shù),中間點的度數(shù)為偶數(shù) 起點和終點:開始和結(jié)束時必須經(jīng)過一條邊,其余情況為:從一條邊

    2024年02月12日
    瀏覽(23)
  • 【單源最短路 圖論】882. 細分圖中的可到達節(jié)點

    【單源最短路 圖論】882. 細分圖中的可到達節(jié)點

    視頻算法專題 單源最短路 圖論 給你一個無向圖(原始圖),圖中有 n 個節(jié)點,編號從 0 到 n - 1 。你決定將圖中的每條邊 細分 為一條節(jié)點鏈,每條邊之間的新節(jié)點數(shù)各不相同。 圖用由邊組成的二維數(shù)組 edges 表示,其中 edges[i] = [ui, vi, cnti] 表示原始圖中節(jié)點 ui 和 vi 之間存在

    2024年04月10日
    瀏覽(26)
  • 圖論算法基礎:單源最短路徑Dijkstra算法分析

    圖論算法基礎:單源最短路徑Dijkstra算法分析

    在 有向帶權(quán)圖 中給定一個起始頂點(源點),Dijkstra算法可以求出 所有其他頂點 到源點的最短路徑,Dijkstra算法 不能用于同時含有正負權(quán)值的邊的圖 Source 頂點集合:已經(jīng)確定 到源點的最短路徑 的頂點就會加入 Source 集合中, Source 集合初始時只有源點 dist 數(shù)組:用于記錄每個頂點到

    2024年02月11日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包