题意:
n个点m条边的图,每条边有一个类别每走一段连续的同类别边花费1,求从1到n的最小花费。
连续同类别边,比如有一个图:
4 4
1 2 1
2 3 1
2 3 2
3 4 1
那么 1->2,2->3,3->4,1->2->3和1->2->3->4和2->3都可以称为一段连续的同类别边,换句话说就是最近走过的几条边的类型相同的话,这么多条边只算1点花费
思路:
枚举边的类别然后使用并查集维护它们的合并情况,如果多点合并即有好几条边是连续的,那么就构造一个新点让这些都和新点连一条花费为1双向边,构造效果比如:
1 2 1
2 3 1
3 4 1
1,2,3,4四点合并于一点,假设构造的新点为x
那么我们会在新图中得到如下边:
1 x 1
x 1 1
2 x 1
x 2 1
3 x 1
x 3 1
4 x 1
x 4 1
对新图跑最短路会发现跑出的答案将会是原图正确答案的2倍,是因为我们构图的时候通过造一个新点实现原来由相同类型边的连接但是我们在新图想要连接的点可以从一点走到另一点就要走两条边即需要花费2的代价去走原来花费1的代价的路
构造完新图就只要跑一个喜闻乐见的堆优化的dijkstra就可以得到答案
C++代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 300010;
const int maxm = 800010;
const int inf = 0x3f3f3f3f;
int n,m,tol,head[maxn];
struct edge
{
int to,cost,next;
}es[maxm];
void addedge( int u , int v , int w )
{
es[tol].to = v;
es[tol].cost = w;
es[tol].next = head[u];
head[u] = tol++;
}
struct mess
{
int from,to,cost;
friend bool operator<( const mess&a , const mess&b )
{
return a.cost<b.cost;
}
}data[maxm>>2];
int dis[maxn],vis[maxn],used[maxm>>3],pre[maxm>>3],id[maxm>>3],sizes;
int Find( int x )
{
if ( x!=pre[x] )
pre[x] = Find( pre[x] );
return pre[x];
}
void Build( int l , int r )
{
for ( int i=l ; i<=r ; i++ )
{
int u = data[i].from;
int v = data[i].to;
used[u] = used[v] = 0;
pre[u] = u,pre[v] = v;
id[u] = id[v] = -1;
}
for ( int i=l ; i<=r ; i++ )
{
int u = data[i].from;
int v = data[i].to;
int fx = Find(u);
int fy = Find(v);
if ( fx!=fy )
pre[fy] = fx;
}
for ( int i=l ; i<=r ; i++ )
{
int u = data[i].from;
int v = data[i].to;
int fx = Find( u );
int fy = Find( v );
if ( !used[u] )
{
if ( id[fx]==-1 )
id[fx] = ++sizes;
addedge( u , id[fx] , 1 );
addedge( id[fx] , u , 1 );
}
if ( !used[v] )
{
if ( id[fy]==-1 )
id[fy] = ++sizes;
addedge( v , id[fy] , 1 );
addedge( id[fy] , v , 1 );
}
}
}
struct node
{
int pos,dis;
friend bool operator<( const node&a , const node&b )
{
return a.dis>b.dis;
}
};
void dijkstra()
{
priority_queue<node>Q;
node p;
p.pos = 1;
p.dis = 0;
Q.push(p);
for ( int i=1 ; i<=sizes ; i++ )
dis[i] = inf,vis[i] = 0;
dis[1] = 0;
while ( !Q.empty() )
{
p = Q.top();
Q.pop();
int u = p.pos;
if ( vis[u] )
continue;
vis[u] = 1;
for ( int i=head[u] ; i!=-1 ; i=es[i].next )
{
int v = es[i].to,w = es[i].cost;
if ( dis[v]>dis[u]+w )
{
dis[v] = dis[u]+w;
p.pos = v;
p.dis = dis[v];
Q.push(p);
}
}
}
}
int main()
{
for ( ; scanf ( "%d%d" , &n , &m )==2 ; )
{
for ( int i=1 ; i<=m ; i++ )
scanf ( "%d%d%d" , &data[i].from , &data[i].to , &data[i].cost );
sort ( data+1 , data+m+1 );
tol = 0;
sizes = n;
memset ( head , -1 , sizeof(head) );
for ( int i=1 ; i<=m ; i++ )
{
int l=i,r=i;
while ( r+1<=m&&data[r+1].cost==data[l].cost )
r++;
Build( l , r );
i = r;
}
dijkstra();
if ( dis[n]==inf )
dis[n] = -2;
printf ( "%d\n" , dis[n]>>1 );
}
return 0;
}