hdu5521 M - Meeting(最短路+dijkstra+虚点)

题意:

给你n个点和m组集合,让你求点1到点n的最短路。

这m组集合中,每行给一个t代表这个强连通集内任两点互相可达的时间,一个s表集合的容量,剩下s个数的是点的标号。

要输出的是一人在1,一人在n,是否存在中途某点,使二人最短时间相遇。

从两端各跑一遍dijkstra,分别记到dis1数组和disn数组中,

在i点实际相遇时间deed=max(dis1[i],disn[i]),再用一个ans去更新deed的最小值就好了。

题解:

我可能是计组没学好...这不就是直接相连变总线么...

建一个虚拟节点,使该节点与这个强连通集内每一节点连边,

向总线传输信息需要t,返回信息需要0(其实反过来也行,但想想似乎不符合超级节点定义)

这就实现了o(m*m)→o(m)的转化。

由于最后包含虚拟节点共有n+m个节点,相当于用1e6的n跑dijkstra。

思路来源:

https://blog.youkuaiyun.com/zcmartin2014214283/article/details/52748596

https://blog.youkuaiyun.com/yuanjunlai141/article/details/52713350

后记:

pli一个ll一个int的操作,以后还是写两个ll吧,真是太麻烦了。

手敲不出来堆优化dijkstra,感觉自己要被退学了。

StatusAccepted
Time2371ms
Memory76100kB
Length2538
LangC++
Submitted2018-09-20 23:53:07
Shared 
RemoteRunId26340319
#include <iostream>
#include <algorithm> 
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <bitset> 
#include <functional>
typedef long long ll;
const ll INF=9999999999999999;
const int mod=1e9+7;
const double eps=1e-7;
const int maxn=2000005;
#define vi vector<int> 
#define si set<int>
#define pli pair<ll,int> 
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a)) 
using namespace std;
struct edge 
{
	int nex,to,w;
}e[maxn];
int head[maxn],cnt,tot,n,m,ans[maxn],num;
ll dis1[maxn],disn[maxn];//1节点跑一遍dijkstra,n节点跑一遍
priority_queue<pli,vector<pli>,greater<pli> >q; 
bool vis[maxn];
void init(int n)
{
	while(q.size())q.pop();
	mem(head,-1);
	mem(ans,0);
	mem(e,0);
	rep(i,0,maxn-1)
	{
		dis1[i]=INF;
		disn[i]=INF;
	}
	num=0;//ans的个数 
	cnt=0;//边标号 
	tot=n;//虚拟节点初始标号 
}
void add(int u,int v,int w)
{
	e[cnt].to=v;
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt++;
}
void dijkstra(ll dis[],int u)
{
  while(q.size())q.pop();
  mem(vis,0);
  for(int i=head[u];~i;i=e[i].nex)
  {
  	int v=e[i].to;
	ll w=e[i].w;
  	dis[v]=w;
  	q.push(pli(dis[v],v)); 
  }
  dis[u]=0;vis[u]=1;
  while(!q.empty())
  {
  	pli tmp=q.top();
  	ll fir=tmp.first;
	int sec=tmp.second;
  	q.pop();
  	if(vis[sec])continue;
  	vis[sec]=1;
  	for(int i=head[sec];~i;i=e[i].nex)
  	{
  		int v=e[i].to;
		ll w=e[i].w;
  		if(dis[v]>dis[sec]+w)
  		{
  			dis[v]=dis[sec]+w;
  			q.push(pli(dis[v],v));
  		}
  	}
  }
}
int main()
{
   int t;
   sci(t);
   rep(k,1,t)
   {
   	sci(n),sci(m);
   	init(n);
   	int t,s;
   	rep(i,0,m-1)
   	{
   		sci(t),sci(s);
   		rep(j,0,s-1)
   		{
   			int u;
			sci(u);u--;
			add(u,tot,t);
			add(tot,u,0); 
   		}
   		tot++;//虚拟节点 
   	}
   	printf("Case #%d: ",k);
   	dijkstra(dis1,0);
   	if(dis1[n-1]==INF)
   	{
   		puts("Evil John");
   		continue;
   	}
   	dijkstra(disn,n-1);
        ll res=INF;
     	rep(i,0,n-1)
        {
   	ll deed=max(dis1[i],disn[i]);
   	res=min(res,deed);
        }
        printf("%lld\n",res);
        rep(i,0,n-1)
     	{
   	ll tmp=max(dis1[i],disn[i]);
        if(tmp==res)ans[num++]=i+1;
    	}
        printf("%lld",ans[0]);
     	rep(i,1,num-1)printf(" %lld",ans[i]);
    	puts("");
   } 
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值