码迷,mamicode.com
首页 > 其他好文 > 详细

【OJ2171】最佳路径

时间:2018-08-02 01:54:42      阅读:151      评论:0      收藏:0      [点我收藏+]

标签:using   转化   ace   需要   strong   while   names   \n   矩阵乘法   

2171 -- 最佳路径(Solution)

题目大意 : 给出一个 \(n\) 个顶点 \(m\) 条边的带权有向图,你要从 \(1\) 号点到 \(n\) 号点,途中你可以进行不超过 \(C\) 次操作:经过一条边时,将这条边的边权取反一次,经过后该边权复原。求 \(1\) 号点到 \(n\) 号点的最短路径。(保证一开始边权为非负,不保证最后答案非负。) \((n≤100,m≤20000,C≤300000)\)

Tag: 最短路、矩阵快速幂

Analysis By LC:

我们先求出不操作和操作一次的最短路径图:我们将原图复制一份,若两点之间有边,则在两图的两点间连一条边权取反的边,即若 \(u\rightarrow v\) 边权为 \(w\) ,则 \(u_1 \rightarrow v_1\) 边权为 \(w\)\(u\rightarrow v_1\) 边权为 \(-w\) ,这样跑一遍最短路即可求出不操作和操作一次的最短路。(详解:你操作一次后,相当于从原图走了一条路径到了复制图,那么原图的点到复制图的点的最短路即为操作一次的最短路。)

那么得到了操作一次的最短路就可以得到操作 \(C\) 次的最短路:因为在操作一次的图上,每经过一个点就相当于进行了一次操作,那么我们的问题就可以转化为:求经过不超过 \(C\) 个点的最短路,这是一类经典的图邻接矩阵快速幂的问题,可以直接用矩阵快速幂求解。需要注意的是 \(C\)\(0\) 的情况,你可以快速幂后再乘上不操作的图,也可以直接特判输出不操作的最短路径。

这是一个很有意思的题目(lzz大佬的题),欢迎大家来玩!

Code By LC :

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=105;
int n,m,c,ma[N*2][N*2];
inline int _read()
{
    char c; int x=0;
    for(;c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
    return x;
}
struct matrix
{
    ll a[N][N];
    matrix()    {
        memset(a,0x3f,sizeof(a));
        for(int i=1;i<=n;i++)
            a[i][i]=0;
    };
    matrix operator * (matrix s) const  { //矩阵乘法改为松弛操作
        matrix ret;
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    ret.a[i][j]=min(ret.a[i][j],a[i][k]+s.a[k][j]);
        return ret;
    }
};
matrix mpow(matrix s, int t) //矩阵快速幂
{
    matrix ret;
    while(t)
    {
        if(t&1) ret=ret*s;
        s=s*s; t>>=1;
    }
    return ret;
}
void floyd() //求不操作和操作一次的最短路
{
    for(int k=1;k<=n*2;k++)
        for(int i=1;i<=n*2;i++)
            for(int j=1;j<=n*2;j++)
                ma[i][j]=min(ma[i][j],ma[i][k]+ma[k][j]);
}
int main()
{
    int T=_read();
    while(T--)
    {
        n=_read(),m=_read(),c=_read();
        memset(ma,0x3f,sizeof(ma));
        for(int i=1;i<=n;i++) ma[i][i]=ma[i][i+n]=0;
        while(m--)
        {
            int u=_read(),v=_read(),w=_read();
            ma[u][v]=ma[u+n][v+n]=min(w,ma[u][v]); //原图复制一份,重新构图
            ma[u][v+n]=min(-w,ma[u][v+n]); //两图间连边权为负的边
        }
        floyd();
        matrix x,y;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                x.a[i][j]=ma[i][j],y.a[i][j]=ma[i][j+n]; //x:不操作的最短路矩阵;y:操作一次……
        matrix p=mpow(y,c);
        x=x*p; //防止C=0,再乘上原矩阵
        printf("%lld\n",x.a[1][n]);
    }
}

【OJ2171】最佳路径

标签:using   转化   ace   需要   strong   while   names   \n   矩阵乘法   

原文地址:https://www.cnblogs.com/farway17/p/9404506.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!