zoukankan      html  css  js  c++  java
  • [洛谷P6190] [NOI Online #1 入门组] 魔法

    题目链接

    题意非常简单,就是在最短路的基础的上加上了可以施魔法(经过这条边的时候,将权值取反,注意只是经过的时候取反,并不是永久的)的操作。

    看到n的范围只有100,很快的就能想到floyd或者分层图

    首先我们分析,每次使用魔法必然能让最短路变短,那么我们一定会将k次魔法用满,那么题意就转化为,恰好使用k次魔法的1到n的最短路。

    在 acwing - 牛站这道题的时候我们知道,类floyd + 矩阵快速幂是可以logn时间内处理出点对间正好经过k条边的最小距离。

    之后我最初的想法是存一个负权矩阵,之后跑floyd,这样相当于是知道点对间用了k次魔法的最短距离,之后将这些距离当作边加入原图,之后跑分层dij (分为使用了负权边与没使用) 。

    跑出样例之后,提交...果不其然0分。(跑出样例的原因好像是因为我都加了双向边,当时没看到是有向边。

    在看了题解之后,我才明白我错在哪里。首先存一个负权矩阵去跑floyd,这一步就不是很对。

    让我们考虑一下,类floyd到底在做什么事情——类floyd在处理点对间正好经过k条边的最小距离

    那么如果我们存一个负矩阵去跑的话,处理出来的是正好经过k条边的最短距离。但是题目求的是正好经过k条边么,不是的它求的是用了k次魔法的最短距离

    也就是说这个使用魔法可以是不连续的,就拿题目中的样例

    2 2 2
    1 2 10
    2 1 1   来说明。 最佳的路线是1 - 2 使用一次魔法,这样当前权值是-10,之后2 - 1不使用魔法,当前权值是-9,之后又通过1 - 2 使用魔法。最终最短距离为-19。

    但是如果我们只丢一个负权矩阵去跑floyd的话,只能得到连续经过k条负权边的最小权值,于是就得不到题目中的样例。

    其实这个更像是一个dp方程,我们通过矩阵快速幂去加速这个过程而已。

    首先我们定义 dp[k][i][j]为使用了k 次魔法,i 到 j 的最小值。

    之后我们发现dp[k][i][j] 可以从所有的dp[k - s][ i ][ t ] + dp[ s ][ t ][ j ]转移过来 ,这个过程与floyd非常相似,于是我们就想到用类floyd的办法去加速这个过程。

    于是我们需要得到使用一次魔法的邻接矩阵,之后对着这个矩阵跑矩阵快速幂,最后就是答案了。

    本质上是一个dp。

    注意特判无法使用魔法的情况。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int inf = 0x3f3f3f3f; ///1061109567
    const int N = 100 + 10;
    
    int n, m, K, u[2500+1], v[2500+1];
    ll f[N][N], g[N][N], t[N][N];
    ll w[2500+1];
    
    void mul(ll c[][N], ll a[][N], ll b[][N]) {
        memset(t, 0x3f, sizeof t);
        for (int k = 1; k <= n; ++ k)
            for (int i = 1; i <= n; ++ i)
                for (int j = 1; j <= n; ++ j)
                    t[i][j] = min(t[i][j], a[i][k] + b[k][j]);
        memcpy(c, t, sizeof t);
    }
    
    void qmi() {
        memset(f, 0x3f, sizeof f);
        for (int i = 1; i <= n; ++ i) f[i][i] = 0;
        while (K) {
            if (K & 1) mul(f, f, g);
            mul(g, g, g);
            K /= 2;
        }
    }
    
    void print2(ll p[][N]) {
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= n; ++ j) {
                printf("%lld ", p[i][j]);
            }
            puts("");
        }
    }
    
    
    int main() {
        scanf("%d%d%d", &n, &m, &K);
        memset(f, 0x3f, sizeof f);
        for (int i = 1; i <= n; ++ i) f[i][i] = 0;
    
        for (int i = 1; i <= m; ++ i) {
            scanf("%d%d%lld", &u[i], &v[i], &w[i]);
            f[u[i]][v[i]] = w[i];
        }
    
        for (int k = 1; k <= n; ++ k)
            for (int i = 1; i <= n; ++ i)
                for (int j = 1; j <= n; ++ j)
                    f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
    
    
        if (K) {
            for (int i = 1; i <= n; ++ i)
                for (int j = 1; j <= n; ++ j)
                    t[i][j] = f[i][j];
    //        print2(t);
            for (int k = 1; k <= m; ++ k)
                for (int i = 1; i <= n; ++ i)
                    for (int j = 1; j <= n; ++ j)
                        t[i][j] = min(t[i][j], f[i][u[k]] + f[v[k]][j] - w[k]);
            memcpy(g, t, sizeof t);
    //        print2(g);
            qmi();
        }
        
        printf("%lld
    ", f[1][n]);
        return 0;
    }
  • 相关阅读:
    java中日期格式的转换和应用
    struts2下载
    struts2上传
    div下拉框(待改善)
    jsp中jquery用法一步刷新 验证用户名是否存在
    react 解析json
    babel-polyfill 解决浏览器兼容
    页面跳转缓存 keepAlive
    webpack知识点
    element-ui upload
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/15438584.html
Copyright © 2011-2022 走看看