链接: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层图就是如下结果:
可以直观看出,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);
}
}