HDU 6386&ARC061E 重构图+最短路

HDU题目链接

ARC题目链接

题意:

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值