HDU 5521 Meeting 【拆点+最短路】

题目链接

题意

给m个由图中结点组成的点集,点集中的点两两连通且距离为相等的ti。现有两人分别从1和N点处同时出发吗,问能否相遇以及相遇的最短时间。

分析

很容易想到直接分别以点1和点N为起始点求最短路,再遍历各个点即可求得最短相遇时间。然而建图上却有问题:这个题中的边是以点集的形式给出,极端情况下可能会出现有1E12条边的稠密图。
这时就要利用点集中的点之间距离相等这个性质,拆点来建图。将点集抽象成一个点,将点集中的每个连一条长为ti/2的边到这个点集的点。 这样做可以大大减少边的数量。另外为了防止出现浮点数,可以直接把每个边变成ti而不是ti/2最后在最短时间上除以2就可以了

AC代码

//HDU 5521 Meeting
//AC 2016-08-10 16:43:30
//Shortest Path
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <set>
#include <string>
#include <map>
#include <queue>
#include <deque>
#include <list>
#include <sstream>
#include <stack>
using namespace std;

#define cls(x) memset(x,0,sizeof x)
#define inf(x) memset(x,0x3f,sizeof x)
#define neg(x) memset(x,-1,sizeof x)
#define ninf(x) memset(x,0xc0,sizeof x)
#define st0(x) memset(x,false,sizeof x)
#define st1(x) memset(x,true,sizeof x)
#define INF 0x3f3f3f3f
#define lowbit(x) x&(-x)
#define input(x) scanf("%d",&(x))
#define bug cout<<"here"<<endl;
//#define debug

int T;
int n,m;

struct node
{
    int pos;
    int dist;
    bool operator< (const node &rhs) const
    {
        return dist>rhs.dist;
    }
}p,q;

vector<node> G[1100100];

int dist1[1100100],dist2[1100100];

void dijkstra(int beg,int dist[])
{
    priority_queue<node> DIJ;
    p.pos=beg;p.dist=0;
    dist[beg]=0;
    DIJ.push(p);
    while(DIJ.size())
    {
        p=DIJ.top();DIJ.pop();
        if(dist[p.pos]>p.dist) continue;
        for(int i=0;i<G[p.pos].size();++i)
        {
            q.pos=G[p.pos][i].pos;
            q.dist=p.dist+G[p.pos][i].dist;
            if(dist[q.pos]>q.dist)
            {
                dist[q.pos]=q.dist;
                DIJ.push(q);
            }
        }
    }
    return;
}

int main()
{
    #ifdef debug
        freopen("E:\\Documents\\code\\input.txt","r",stdin);
        freopen("E:\\Documents\\code\\output.txt","w",stdout);
    #endif
    int T;input(T);
    for(int kase=1;kase<=T;++kase)
    {
        scanf("%d %d",&n,&m);
        for(int i=0;i<=n+m;++i) G[i].clear();
        int t,s,a;
        for(int i=1;i<=m;++i)
        {
            scanf("%d %d",&p.dist,&s);
            for(int j=0;j<s;++j)
            {
                input(a);p.pos=i+n;
                G[a].push_back(p);
                p.pos=a;
                G[i+n].push_back(p);
            }
        }
        inf(dist1);inf(dist2);
        dijkstra(1,dist1);dijkstra(n,dist2);
        int res=INF;
        for(int i=1;i<=n;++i)
            res=min(res,max(dist1[i],dist2[i]));
        printf("Case #%d: ",kase);
        if(res>=INF) printf("Evil John\n");
        else
        {
            printf("%d\n",res/2);
            bool first=1;
            for(int i=1;i<=n;++i)
            {
                if(max(dist1[i],dist2[i])==res)
                {
                    if(!first) putchar(' ');
                    first=0;
                    printf("%d",i);
                }
            }
            putchar('\n');
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/DrCarlluo/p/6580590.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值