G. Bicycles
题意:在一个无向图里你要从1点到达n点,每条路的路径长度是该路的权值乘于你当前的慢度因子。而在每个点上我们都有一个慢度因子可以进行更换,问你到达n点所需要的最短时间。
思路:我们很容易想到每次遇到更小的慢度因子我们就要更换,但因为存在你先去绕远路拿更小的慢度因子然后再去终点答案更优于直接找一条最短路的情况。所以我们可以用d[点][因子]这种二维形式来表示每个点上我们用当前这个因子跑的距离。然后跑一遍dij算法,每次拓展当前因子的最优答案,最后从d[n][i]遍历到1000就能找到最优解。
注意下方代码折叠!~~~~
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 1e18;
const int N = 1e3+100;
int n,m,w[N],d[N][N];
bool vis[N][N];
struct node{//建图用
int z,leh;
};
vector<node> h[N];
void add(int u,int v,int z)
{
h[u].push_back({v,z});
h[v].push_back({u,z});
}
struct edge{//根堆结构体
int num,len;
bool operator > (const edge &t) const
{
return len>t.len;
}
int v;
};
void init()//初始化
{
for(int i=0;i<=n;i++){
for(int j=0;j<=1000;j++){
vis[i][j]=0,d[i][j]=inf;
}
}
for(int i=0;i<=n;i++) h[i].clear();
}
void dij(int x)
{
priority_queue<edge,vector<edge>,greater<edge> > q;
q.push({x,0,w[x]});
d[x][w[x]]=0;
while(q.size())
{
auto t=q.top();
q.pop();
int u=t.num,sped=t.v,ds=t.len;
if(vis[u][sped]==1) continue;
vis[u][sped]=1;
for(auto p:h[u])
{
int v=p.z,dd=p.leh;
int newsp=min(sped,w[v]);//替换更小的因子
if(d[v][newsp]>d[u][sped]+dd*sped)//拓展边
{
d[v][newsp]=d[u][sped]+dd*sped;
q.push({v,d[v][newsp],newsp});
}
}
}
}
void solve()
{
int res=1e18;
cin>>n>>m;
init();
for(int i=1;i<=m;i++)
{
int u,v,z;
cin>>u>>v>>z;
add(u,v,z);
}
for(int i=1;i<=n;i++) cin>>w[i];
dij(1);
for(int i=0;i<=1000;i++)//因子最大为1000,直接遍历寻找答案
{
res=min(res,d[n][i]);
}
cout<<res<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
{
solve();
}
return 0;
}
注意:小根堆因为是开的结构体所以要重载,把最小距离放在堆顶就是堆优化dij的贪心思路,但就算把最大距离放在堆顶也能AC就是会多跑点时间。
结语:此题难度不大,应该算是dij入门类的题目,但是第一次遇到应该会想一会。主要就是想到怎么用二维的形式去表示dij从原本一个距离变成距离和速度两个因素。