(字节跳动冬令营网络赛)H-Accel World(弗洛伊德最短路+图DP)

题目链接https://ac.nowcoder.com/acm/contest/296/H

题目描述

Chiaki was trapped in a strange place which can be regarded as a connected undirected graph with n vertices (numbered from 1 to n) and m weighted edges (numbered from 1 to m). In the beginning, Chiaki is at vertex 1 with speed v equals to 1 unit per second and would like to go to the vertex n.
There are some special vertices in the graph, called \textit{acceleration vertex}. Once Chiaki reached an acceleration vertex, her speed will be doubled: from v to 2v. The same acceleration vertex can be used multiple times to achieve multiple acceleration, but it only works under this limitation: the last acceleration vertex Chiaki visited should not equal to the current acceleration vertex Chiaki reached.
For example, vertex 2 and 3 are acceleration vertices while 1, 4 and 5 are not. If Chiaki chooses the path [Math Processing Error], Chiaki's speed would be accelerated for three times (8 unit per second in the end). But if the path is [Math Processing Error], Chiaki would have only one acceleration.
Chiaki would like to know the minimum time needed to reach the vertex n.

Input

There are multiple test cases. The first line of the input contains an integer T, indicating the number of test cases. For each test case:
The first line contains three integers n, m and k (2 ≤ n ≤ 100, 1 ≤ m ≤ 8000, 0 ≤ k ≤ n) -- the number of vertices, the number of edges and the number of special vertices.
Each of the following m lines contains three integers ui, vi and wi (1 ≤ ui, vi ≤ n, 1 ≤ wi ≤ 1000) denoting an edge with wi unit length connecting ui and vi.
The next line contains k integers p1,p2,...,pk (1 ≤ pi ≤ n) denoting the index of each \textit{acceleration vertex}.
It's guaranteed that the sum of n over all test cases will not exceed 1000 and the sum of m over all test cases will not exceed 80000.

Output

For each test case, output a real number t denoting the minimum time to reach the vertex n and an integer s denoting the maximum times of acceleration Chiaki can achieve among all optimal solutions.
Your answer for t will be considered correct if and only if the absolute error or relative error of your answer is less than 10-8. And by the way, if s is greater than 32767, output ``Burst!'' (without the quotes) instead.

示例

输入

3
2 1 2
1 2 1
1 2
5 4 2
1 2 1
2 3 1
3 4 1
4 5 1
2 3
6 7 2
1 2 2
2 4 2
4 6 2
1 3 2
3 4 2
4 5 4
5 6 4
3 4

输出

0.5000000000 2
2.0000000000 Burst!
3.5000000000 2


题目大意

在一张无向图连通图中,每条边都有一定的长度,通过它需要长度除速度的时间,图中有一些加速点,在不是连续通过它时会使速度加倍,最终的速度不能超过32767,否则输出Burst!

题目思路

由题意,我们首先求出所有的点之间在速度为1时通行所需的最小时间,记为dist[i][j]。首先我们通过分析可以得到,到达终点所需的最短时间有两种可能:不经过加速点就到达终点,经过加速点最终到达终点。对于不通过加速点就到达终点的情况,我们可以通过预处理的最短路很方便地解决。而经过加速点以及经过多少、哪些加速点能使所用时间达到最短则需要对加速点进行DP,为此我们起点到所有加速点的时间记为dp[j],其中j为加速点在加速点中的编号。对于本题,所有经过加速点到达终点的时间都可以分解为:

\[T=dp[j]+\frac{dist[point[j]][n]}{加速次数} \]

但是到达一个加速点之前可能还经过其他的加速点,因此我们还需要对起点到加速点的时间进行DP,为此为DP添加一维,记为加速次数,则有如下状态转移方程:

\[dp[i+1][l]=min(dp[i+1][l],dp[i][j]+\frac{dist[point[i]][point[j]]}{2}) \]

这样对加速的次数不断迭代就可以算出最短所需的时间。这样问题就算解决了一半了,但是应该怎么怎么计算是否超速呢?
我们把经过一些点的集合后总时间减短的点称为加速点集。我们不难发现凡是能加速到2^15速度的加速点集都可以继续加速以期达到更大的速度,因此凡是加速次数大于15最终也会大于100,而一个加速点集最大的加速点数即为100,且所有的加速点集的倍数中总有介于100与200之间的,因此我们只需动态规划200论,最终加速次数假如大于100次,那么一定会超速


AC代码

#include<cstring>
#include<string>
#include<iostream>
#include<cstdio>
using namespace std;
const int N=105;
const int M=8005;
struct BigInt
{
    unsigned  t[8];
    BigInt() {}
    BigInt(int x)
    {
        memset(t,0,sizeof t);
        t[0]=x;
    }
    long double g()
    {
        long double  x=0;
        for(int i=7;i>=0;--i)
            x*=1<<30,x+=t[i];
        return x;
    }
};
BigInt operator + (BigInt a,BigInt b)
{
    BigInt c;
    for(int i=0;i<8;++i)
    {
        unsigned t=a.t[i]+b.t[i];
        if((t>>30)&&i!=7)
            ++a.t[i+1],t-=1u<<30;
        c.t[i]=t;
    }
    return c;
}
BigInt Dou(BigInt t) {return t+t;}
bool operator < (BigInt a,BigInt b)
{
    for(int i=7;i>=0;--i)
        if(a.t[i]!=b.t[i]) return a.t[i]<b.t[i];
    return 0;
}
bool operator <= (BigInt a,BigInt b)
{
    for(int i=7;i>=0;--i)
        if(a.t[i]!=b.t[i]) return a.t[i]<b.t[i];
    return 1;
}
int dis[N][N];
BigInt dp[250][N];
int point[N];
BigInt dist[N][N];
int main()
{
    int t;
    scanf("%lld",&t);
    BigInt inf;
    for(int i=0;i<7;++i) inf.t[i]=(1<<30)-1;
    inf.t[7]=(1<<29)-1;
    while(t--)
    {
        int n,m,q;
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
        dis[i][j]=1e9;
        for(int i=1;i<=n;i++) dis[i][i]=0;
        for(int i=1;i<=m;i++)
        {
            int u,v;int w;
            scanf("%d%d%d",&u,&v,&w);
            dis[u][v]=dis[v][u]=min(dis[u][v],w);
        }
        for(int k=1;k<=n;k++)
        {
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=n;j++)
                {
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
                }
            }
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            dist[i][j]=BigInt(dis[i][j]);
        }
        for(int i=1;i<=q;i++)
        {
            scanf("%d",&point[i]);
            dp[1][i]=Dou(dist[1][point[i]]);
        }
        BigInt ans=dist[1][n];
        int k=0;
        double pownum=1;
        for(int i=1;i<=110;i++)
        {
            ans=Dou(ans);
            pownum*=2;
            for(int j=1;j<=q;j++)
            {
                if(dp[i][j]+dist[point[j]][n]<=ans){
                    ans=dp[i][j]+dist[point[j]][n];
                    k=i;}
                dp[i+1][j]=inf;
            }
            for(int j=1;j<=q;j++)
            {
                for(int l=1;l<=q;l++)
                {
                    if(j!=l)
                    {
                        dp[i+1][l]=min(dp[i+1][l],Dou(dp[i][j]+dist[point[j]][point[l]]));
                    }
                }
            }
        }
        printf("%.10lf ",(double)ans.g()/pownum);
        if(k>=103)
        {
             puts("Burst!");
        }
        else printf("%d\n",k);
    }
}
posted @ 2018-12-26 20:46  Fly_White  阅读(345)  评论(0编辑  收藏  举报