zoukankan      html  css  js  c++  java
  • 最短路

    1.Dijkstra:单源最短路

    bool vis[MAXN];
    int d[MAXN];
    priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > Q;
    void Dijestra(int begin){
        memset(d, 127, sizeof(d));
        d[begin] = 0;
        Q.push(make_pair(0, begin));
        while(!Q.empty()){
            int x = Q.top().second;
            int cost = Q.top().first;
            Q.pop();
            if(vis[x]) continue;
            vis[x] = true;
            for(int l=head[x]; l; l=next[l]){
                int y = last[l];
                if(!vis[y] && d[y] > val[l] + cost)
                    d[y] = val[l] + cost, Q.push(make_pair(d[y], y));
            }
        }
    }

    时间复杂度:$O((n+m)log(n+m))$

    2.SPFA:单源最短路,判断负环

    int d[MAXN];
    bool inQ[MAXN];
    queue<int> Q;
    void SPFA(int begin){
        memset(d, 127, sizeof(d));
        Q.push(begin), inQ[begin] = true, d[begin] = 0;
        while(!Q.empty()){
            int x = Q.front();
            inQ[x] = false, Q.pop();
            for(int l=head[x]; l; l=next[l]){
                int y = last[l];
                if(d[y] > d[x] + val[l]){
                    d[y] = d[x] + val[l];
                    if(!inQ[y]) Q.push(y), inQ[y] = true;
                }
            }
        }
    }

    时间复杂度:随机数据$O(km)$,最坏情况$O(nm)$

    3.Floyd:多源最短路,矩阵乘法

    for(int k=1; k<=n; k++)
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);

    时间复杂度:$O(n^3)$

    经典例题:

    1.在Dp转移决策需要预处理时,可将一个Dp看作图中一个点,决策代价看作边权,起始状态看作出发点,最终状态看作终点。

    例题:Online 1503 BZOJ2259 新型计算机

    Dp转移方程十分容易推:$Dp[i] = min(Dp[i+a[i]+1], Dp[j]+abs(a[i]-(i-j-1)))$,然而转移却需要$O(n)$的时间,面对$n<=10^6$的数据是过不了的(如果用线段树可以$O(logn)$转移)

    看到这里,我想到用图论,在$i$和$i+a[i]+1$之间连一条长度为$0$的边,从$i+a[i]+1$开始向两边的点连长度为$1$的边(如果已经连过就跳过),平均连边时间复杂度为$O(n)$,最多不超过4n条点,起点$0$,终点为$n+1$,求最短路即可。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <queue>
    using namespace std;
    
    long long read(){
        long long ans = 0, f = 1;
        char ch = getchar();
        while(!isdigit(ch))
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
        while(isdigit(ch));
        return ans * f;
    }
    
    const int MAXN = 1000003, MAXM = 4 * MAXN;
    int head[MAXN], next[MAXM], last[MAXM], val[MAXM], lineNum = 0;
    void add(int x,int y,int v){
        lineNum++, next[lineNum] = head[x], head[x] = lineNum, last[lineNum] = y, val[lineNum] = v;
    }
    
    int a[MAXN];
    bool conneL[MAXN], conneR[MAXN], inQ[MAXN];
    
    bool vis[MAXN];
    int d[MAXN];
    priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > Q;
    void Dijestra(int begin){
        memset(d, 127, sizeof(d));
        d[begin] = 0;
        Q.push(make_pair(0, begin));
        while(!Q.empty()){
            int x = Q.top().second;
            int cost = Q.top().first;
            Q.pop();
            if(vis[x]) continue;
            vis[x] = true;
            for(int l=head[x]; l; l=next[l]){
                int y = last[l];
                if(!vis[y] && d[y] > val[l] + cost)
                    d[y] = val[l] + cost, Q.push(make_pair(d[y], y));
            }
        }
    }
    
    int main(){
        int n = read();
        for(int i=1; i<=n; i++)
            a[i] = read();
        for(int i=1; i<=n; i++){
            if(i+a[i] <= n)
                add(i, i+a[i]+1, 0);
            if(i+a[i] > n){
                add(i, n+1, a[i]+i-n);
                continue;
            }
            for(int j=a[i]+i+1; j>i && !conneL[j]; j--)
                add(j, j-1, 1), conneL[j] = true;
            for(int j=a[i]+i+1; j<=n && !conneR[j]; j++)
                add(j, j+1, 1), conneR[j] = true;
        }
        Dijestra(1);
        printf("%d", d[n+1]);
        return 0;
    }
    View Code

    2.Dp转移决策无法确定是否计算时

    例题:Online 1292 NOIP2018模拟 过河

    这道题的关键转移的决策不好推,一般的Dp都是顺序递推,而像这道题的决策却有很多情况,考虑用图论,只需要保证图中存在一条路径是正确答案即可。

    首先从假想的原点连到可以到岸边的板子上,对终点同样考虑,之后考虑版子之间的连接,最后对于一个板子,可以花费一些代价使自己变得更大,同样进行连边(重要),因为你不可能两次经过同一个板子,所以花费代价再跳到其他板子是正确的决策。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #include <cstring>
    using namespace std;
    
    const int MAXN = 255,MAXPOINT = 255*255,MAXLINE = 255*255*255*2;
    
    struct point
    {
        long long X,Y;
    };
    struct code
    {
        long long R,V;
    };
    bool cmp(code a1,code a2)
    {
        if(a1.R == a2.R)
            return a1.V < a2.V;
        else
            return a1.R > a2.R;
    }
    int head[MAXPOINT], next[MAXLINE], value[MAXLINE], last[MAXLINE], LineNum = 0;
    int PointNum, CodeNum, Breath;
    long long Cost[MAXPOINT];
    point P[MAXN];
    code C[MAXN];
    
    void add(int x,int y,int v)
    {
        LineNum++, value[LineNum] = v,last[LineNum] = y,next[LineNum] = head[x], head[x] = LineNum;
    }
    
    void init()
    {
        memset(head, 0, sizeof(head));
        LineNum = 0;
        memset(Cost, 127, sizeof(Cost));
        scanf("%d%d%d",&PointNum,&CodeNum,&Breath);
        for(int i=1; i<=PointNum; i++)
            scanf("%lld%lld",&P[i].X,&P[i].Y);
        for(int i=1; i<=CodeNum; i++)
            scanf("%lld%lld",&C[i].R,&C[i].V);
        return;
    }
    
    void preduct()
    {
        sort(C+1, C+1+CodeNum, cmp);
        bool cut[CodeNum+1];
        long long cost = C[1].V;
        for(int i=2; i<=CodeNum; i++)
        {
            if(C[i].R == C[i-1].R)
                cut[i] = true;
            else if(C[i].V >= cost)
                cut[i] = true;
            else
                cost = C[i].V, cut[i] = false;
        }
        int p1 = 1, p2 = 1;
        while(p2 <= CodeNum)
        {
            if(p1 != p2)
                C[p1] = C[p2];
            p1++, p2++;
            while(p2 <= CodeNum && cut[p2])
                p2++;
        }
        CodeNum = p1-1;
    }
    
    void addLine()
    {
        for(int i=1; i<=PointNum; i++)
        {
            for(int j=1; j<=PointNum; j++)
            {
                if(i == j)
                    continue;
                int r = CodeNum;
                long long d = (P[i].Y-P[j].Y)*(P[i].Y-P[j].Y) + (P[i].X-P[j].X)*(P[i].X-P[j].X);
                for(int k=1; k<=CodeNum; k++)
                {
                    while(r>0 && (C[r].R+C[k].R)*(C[r].R+C[k].R) < d)
                        r--;
                    if(r != 0)
                        add((i-1)*CodeNum+k, (j-1)*CodeNum+r, C[r].V);
                }
            }
        }
        for(int i=1; i<=PointNum; i++)
            for(int j=CodeNum-1; j>=1; j--)
                add((i-1)*CodeNum+j+1, (i-1)*CodeNum+j, C[j].V-C[j+1].V);
        for(int i=1; i<=PointNum; i++)
        {
            for(int j=1; j<=CodeNum; j++)
            {
                if(C[j].R < P[i].Y)
                    break;
                add(0, (i-1)*CodeNum+j, C[j].V);
            }
        }
        for(int i=1; i<=PointNum; i++)
        {
            for(int j=1; j<=CodeNum; j++)
            {
                if(C[j].R < Breath - P[i].Y)
                    break;
                add((i-1)*CodeNum+j, CodeNum*PointNum+1, 0);
            }
        }
    }
    
    void SPFA()
    {
        queue<int> q;
        q.push(0);
        Cost[0] = 0;
        while(!q.empty())
        {
            int t = q.front();
            q.pop();
            for(int i = head[t]; i; i = next[i])
            {
                if(Cost[last[i]] > Cost[t] + value[i])
                {
                    Cost[last[i]] = Cost[t] + value[i];
                    q.push(last[i]);
                }
            }
        }
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            init();
            preduct();
            addLine();
            SPFA();
            if(Cost[CodeNum * PointNum + 1] == 9187201950435737471)
                printf("impossible
    ");
            else
                printf("%lld
    ",Cost[CodeNum * PointNum + 1]);
        }
        return 0;
    }
    View Code

    3.Floyd与矩阵乘法的结合

    例题:Online 1119 奶牛接力

    看着Floyd的代码,是否觉得有些熟悉,这不就是矩阵乘法的翻版嘛。其实从意义上来说,如果结果单独存在一个数组中,就有了新的意义——$Dp[i][j]$表示从$i$到$j$经历$k$个点后的图的连通性,而且从意义上来看,矩阵乘法满足结合律(当然不一定满足交换率),所以可以使用快速幂。

    #include <iostream>
    #include <cstdio>
    #include <map>
    #include <cstring>
    #include <climits>
    using namespace std;
    
    long long read()
    {
        long long ans = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
        while(ch >= '0' && ch <= '9');
        return ans * f;
    }
    
    class Matrix
    {
        public:
        long long ** value;
        int size;
        void reset(int size)
        {
            this->size = size;
            value = new long long * [size];
            for(int i=0; i<size; i++)
                value[i] = new long long [size];
        }
        Matrix(int size)
        {
            reset(size);
        }
        void operator = (const int * a)
        {
            int * index = (int *)a;
            for(int i=0; i<size; i++)
                for(int j=0; j<size; j++)
                    value[i][j] = *index, index++;
        }
        void operator *= (Matrix &other)
        {
            long long ans[size][size];
            for(int i=0; i<size; i++){
                for(int j=0; j<size; j++){
                    ans[i][j] = LLONG_MAX >> 1;
                    for(int k=0; k<size; k++)
                        ans[i][j] = min(ans[i][j], value[i][k] + other.value[k][j]);
                }
            }
            for(int i=0; i<size; i++)
                for(int j=0; j<size; j++)
                    value[i][j] = ans[i][j];
        }
        void operator = (Matrix &other)
        {
            for(int i=0; i<size; i++)
                for(int j=0; j<size; j++)
                    value[i][j] = other.value[i][j];
        }
        void operator ^= (long long n)
        {
            Matrix unit(size);
            unit = (*this);
            bool flag = false;
            while(n){
                if(n & 1){
                    if(!flag)
                        flag = true, (*this) = unit;
                    else
                        (*this) *= unit;
                }
                unit *= unit;
                n >>= 1;
            }
        }
    };
    
    const int MAXN = 101;
    int l[MAXN*2], a[MAXN*2], b[MAXN*2];
    
    int main()
    {
        int n = read(), T = read(), begin = read(), end = read();
        int pointNum = 0;
        map<int,int> m;
        for(int i=1; i<=T; i++)
        {
            l[i] = read(), a[i] = read(), b[i] = read();
            if(m.find(a[i]) == m.end())
                m[a[i]] = pointNum++;
            if(m.find(b[i]) == m.end())
                m[b[i]] = pointNum++;
        }
        int a_unit[pointNum][pointNum];
        memset(a_unit, 127, sizeof(a_unit));
        for(int i=1; i<=T; i++)
            a_unit[m[a[i]]][m[b[i]]] = a_unit[m[b[i]]][m[a[i]]] = l[i];
        Matrix unit(pointNum);
        unit = a_unit[0];
        unit ^= n;
        printf("%d",unit.value[m[begin]][m[end]]);
        return 0;
    }
    View Code

    同样还有一道例题:Online 1118 沼泽鳄鱼 也是用同样的思路

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <climits>
    using namespace std;
    
    const int MOD = 10000;
    long long read(){
        long long ans = 0, f = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
            f *= (ch == '-') ? -1 : 1, ch = getchar();
        do ans = ((ans << 1) + (ans << 3) + (ch ^ 48)), ch = getchar();
        while(ch >= '0' && ch <= '9');
        return ans * f;
    }
    
    const int MAXN = 50;
    struct Matrix{
        int size;
        long long value[MAXN][MAXN];
        Matrix(int SIZE = 0){
            size = SIZE;
            memset(value, 0, sizeof(value));
        }
        void normal(){
            for(int i=0; i<size; i++)
                for(int j=0; j<size; j++)
                    value[i][j] = (i == j) ? 1 : 0;
        }
        void operator = (long long * a){
            long long * index = a;
            for(int i=0; i<size; i++)
                for(int j=0; j<size; j++)
                    value[i][j] = *index, index++;
        }
        void operator *= (Matrix &other){
            long long ans[size][size];
            memset(ans, 0, sizeof(ans));
            for(int i=0; i<size; i++)
                for(int j=0; j<size; j++)
                    for(int k=0; k<size; k++)
                        ans[i][j] = (ans[i][j] + value[i][k] * other.value[k][j]) % MOD;
            for(int i=0; i<size; i++)
                for(int j=0; j<size; j++)
                    value[i][j] = ans[i][j];
        }
        void operator = (Matrix &other){
            size = other.size;
            memcpy(value, other.value, sizeof(value));
        }
        void operator ^= (long long n){
            Matrix unit = (*this);
            this->normal();
            while(n){
                if(n & 1)
                    (*this) *= unit;
                unit *= unit;
                n >>= 1;
            }
        }
        void print()
        {
            for(int i=0; i<size; i++){
                for(int j=0; j<size; j++)
                    printf("%d ",value[i][j]);
                putchar('
    ');
            }
        }
    };
    
    int main(){
        int N = read(), M = read(), Start = read(), End = read(), K = read();
        Matrix g[12];
        for(int i=0; i<12; i++){
            g[i].size = N;
            memset(g[i].value, 0, sizeof(g[i].value));
        }
        for(int i=1; i<=M; i++){
            int x = read(), y = read();
            for(int j=0; j<12; j++){
                g[j].value[x][y] = g[j].value[y][x] = 1;
            }
        }
        int NFish = read();
        for(int i=1; i<=NFish; i++){
            int T = read();
            int w[T];
            for(int j=0; j<T; j++)
                w[j] = read();
            for(int j=1; j<=12; j++)
                for(int k=0; k<N; k++)
                    g[j-1].value[k][w[j%T]] = 0;
        }
        Matrix ans(N);
        ans.normal();
        for(int i=0; i<12; i++)
            ans *= g[i];
        ans ^= (K / 12);
        for(int i=1; i<=K%12; i++)
            ans *= g[i-1];
        printf("%lld",ans.value[Start][End]);
        return 0;
    }
    View Code
  • 相关阅读:
    【博弈论】取火柴游戏
    【贪心】马拉松接力赛
    【贪心】0/1背包
    【贪心】赶作业
    C# 设计模式 (一)
    C# 反射入门
    C# WinForm 访问webService天气预报
    WebService 测试地址
    C# Windows服务相关
    CSS 案例学习
  • 原文地址:https://www.cnblogs.com/PHDHD/p/12213766.html
Copyright © 2011-2022 走看看