题目描述
企鹅国中有N座城市,编号从1到N。
对于任意的两座城市i和j,企鹅们可以花费(i xor j)*C的时间从城市ii走到城市jj,这里C为一个给定的常数。
当然除此之外还有M条单向的快捷通道,第i条快捷通道从第F个城市通向第T个城市,走这条通道需要消耗V
的时间。
现在来自Penguin Kingdom University的企鹅豆豆正在考虑从城市A前往城市B最少需要多少时间?
输入描述:
输入第一行包含三个 整数N,M,C,表示企鹅国城市的个数、快捷通道的个数以及题面中提到的给定的常数C。
接下来的M行,每行三个正整数Fi,Ti,Vi(1≤Fi≤N,1≤Ti≤N,1≤Vi≤100),分别表示对应通道的起点城市标号、终点城市标号和通过这条通道需要消耗的时间。
最后一行两个正整数A,B(1≤C≤100),表示企鹅豆豆选择的起点城市标号和终点城市标号。
输出描述:
输出一行一个整数,表示从城市 A 前往城市 B 需要的最少时间。
示例1
输入
4 2 1
1 3 1
2 4 4
1 4
输出
5
说明
直接从 1 走到 4 就好了。
示例2
输入
7 2 10
1 3 1
2 4 4
3 6
输出
34
说明
先从 3 走到 2 ,再从 2 通过通道到达 4 ,再从 4 走到 6。
思路:
思路参考:https://blog.nowcoder.net/n/3cb5429047ce456a811e65c458c01052
但是大佬的代码依旧有问题,即24行i应该从0开始循环,已在本题中改正、
有两个重要的点:
- 不能无脑加边
- i需要从0开始遍历
针对第一个问题大家直接看大佬的解释就好。
针对第二个问题做如下解释:
若n 为4且不存在快速单向边的情况,如果i从1开始,那么就不会存在某个点与4差一位,即没有边到达4。
而若想与4连边,即与4差一位,只能是0,所以0一定要存在。
这个时候会产生两个疑问,第一个疑问是加入了0点,会不会出现“经过了0的中转导致两点间的距离变小”从而出现了错误的结果,第二个疑问是如果某个点想到达点2n,经过0的中转,会不会导致这个点到点2n的距离变大呢?
归结起来可以总结为,这种操作是否会造成距离的缩小或者增大。
针对第一个疑问的证明如下:
设求点u到点v的距离,经过0的中转,两点的距离可以表示为
u
⨁
0
+
0
⨁
v
即
u
+
v
u⨁0 + 0⨁v即u+v
u⨁0+0⨁v即u+v而已知
u
+
v
>
=
u
⨁
v
u+v>=u⨁v
u+v>=u⨁v,故不会造成距离的缩小。
针对第二个疑问证明如下:
先说结论:设某个点为u,标号为
2
n
2^n
2n的点标号为v,那么u+v=u⨁0 + 0⨁v。
为什么这个结论一定会成立呢?
已知若两个数按照二进制排列,若每一位上的数都满足(一个是1一个是0)或者(都为0)在这个前提下,u⨁v=u+v一定成立,例如(10011)⨁(01000)=(11011)换算成十进制为27,而19+8=27。
综上,如果n为2的幂次,那么如果不添加0为中转点,就会导致n点无法到达。如果添加了0作为中转点,那么每一个到达0的点,都可以到达n点,且经过中转的距离与直接到达n的距离相等。
代码:
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e6+10;
int e[N],ne[N],w[N],h[N],idx=0;
typedef pair<int,int>PII;
int n,m,c;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
w[idx]=c;
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int dijkstra(int s,int t)
{
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
priority_queue<PII, vector<PII>, greater<PII>> heap;
heap.push({0, s}); // first存储距离,second存储节点编号
while (heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second, distance = t.first;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];
if (dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j], j});
}
}
}
return dist[t];
}
int main()
{
memset(h,-1,sizeof h);
cin>>n>>m>>c;
for(int i=0;i<m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
for(int i=0;i<=n;i++)
{
for(int j=1;j<=n;j<<=1)
{
int u=i;
int v=i^j;
int w=j*c;
if(v>n)
{
continue;
}
add(u,v,w);
}
}
int s,t;
cin>>s>>t;
cout<<dijkstra(s,t);
}