2019牛客网暑期多校赛第四场J题--free

链接:https://ac.nowcoder.com/acm/contest/884/J
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述
Your are given an undirect connected graph.Every edge has a cost to pass.You should choose a path from S to T and you need to pay for all the edges in your path. However, you can choose at most k edges in the graph and change their costs to zero in the beginning. Please answer the minimal total cost you need to pay.
输入描述:

The first line contains five integers n,m,S,T,K.

For each of the following m lines, there are three integers a,b,l, meaning there is an edge that costs l between a and b.

n is the number of nodes and m is the number of edges.

输出描述:

An integer meaning the minimal total cost.

示例1
输入

3 2 1 3 1
1 2 1
2 3 2

输出

1

备注:

1≤n,m≤103,1≤S,T,a,b≤n,0≤k≤m,1≤l≤1061 \le n,m \le 10^3,1 \le S,T,a,b \le n,0 \le k \le m,1 \le l \le 10^61≤n,m≤103,1≤S,T,a,b≤n,0≤k≤m,1≤l≤106.
Multiple edges and self loops are allowed.

题意:输入n,m,s,t,k,表示一张图,有n个点,m条无向边,起点s和终点t,你可以选择把这张图中最多k条边的权值赋为0,现在问从s到t最短距离为多少。

题解:明显的分层图最短路模板题,唯一需要注意的就是数据比较大,虽然n,m<=1000,k<=m,但是在建立分层图时就是n*k,可以达到1e6这么大的数据范围,然后每次建图都是4条边一起建图,所以边数可以达到4e6这么大,所以我们直接将边数定义成5e5这么大。

分层图最短路简介:
一张n个点,m条边的图中,你可以免费通过k条边,如何决策哪k条边使得从起点s到终点t的距离最短。
比如数据:
(n,m,k)
4 3 2
(m条边)
1 2 2
2 3 3
3 4 2

建立k+1层图就是如下结果:

2019牛客网暑期多校赛第四场J题--free_第1张图片可以直观看出,k条免费通过的边就对应了k+1层图,那么s到t的最短距离就是s到t,t+n,t+2n的最短距离中最短的那条,因为每次从某层到相邻的另一层,都消耗了一次免费通过的机会,而k+1层就至多消耗了k次满足了题目要求,而且注意图,从第1层开始,只能不断往下一层走,保证了每个顶点不会被重复通过,然后根据这个特性建图,剩下的交给迪杰斯特拉处理就好,然后最后依次遍历dis[t],dis[t+n],dis[t+2n]找最短就行了。

AC代码(模板代码)

#include
using namespace std;
//注意数据范围
const int N=5e6+100;
struct Node{
    int v,next,w;
}edge[N];
int head[N],cnt;
void add_edge(int u,int v,int w)
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
bool vis[N];
int dis[N];
struct Sode{
    int v;
    int dis;
    Sode(int v1,int dis1)
    {
        v=v1;
        dis=dis1;
    }
    bool operator<(const Sode &t)const
    {
        return dis>t.dis;
    }
};
priority_queueq;
void dij(int s)
{
    while(!q.empty())
    q.pop();
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f3f3f,sizeof(dis));
    dis[s]=0;
    q.push(Sode(s,0));
     
    while(!q.empty())
    {
        Sode t=q.top();
        q.pop();
        //通过优先队列找到当前距离源点最小的终点
        //如果这个点v已经判断过了,也就是说已经固定为最短路了,就直接跳过
        if(vis[t.v])continue;
        vis[t.v]=true;
        for(int i=head[t.v];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            int w=edge[i].w;
            //直接判断距离大小
            if(vis[v]==false&&dis[v]>dis[t.v]+w)
            {
                dis[v]=dis[t.v]+w;
                q.push(Sode(v,dis[v]));
            }
        }
    }
}
int main()
{
    int n,m,k,s,t;
    while(~scanf("%d%d%d%d%d",&n,&m,&s,&t,&k))
    {
        memset(head,-1,sizeof(head));
        cnt=0;
        while(m--)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            for(int i=0;i<=k;i++)
            {
            	//每一层的点建边
                add_edge(x+i*n,y+i*n,z);
                add_edge(y+i*n,x+i*n,z);
                if(i!=k)
                {
                	//相邻两层之间建立边联系,第i层只能到第i+1层
                    add_edge(x+i*n,y+(i+1)*n,0);
                    add_edge(y+i*n,x+(i+1)*n,0);
                }
            }
        }
        //迪杰斯特拉处理
        dij(s);
        int mi=1e9;
        //找最短中的最短
        for(int i=0;i<=k;i++)
        mi=min(mi,dis[t+i*n]);
        printf("%d\n",mi);
    }
}

你可能感兴趣的:(分层最短路)