题意:给出n个点的有向图,问其中k个点两两之间最小的最短路。
题解:枚举所有点对的情况,做SPFA。如何枚举?通过枚举前半部分点集和后半部分点集,通过SPFA计算前半部分点与后半部分点之间所有最短路的情况,然后剩下没有匹配的点为,前半部分与前半部分,后半部分与后半部分,然后不断地分割点集,直到单个点,总共的层数为logn层。
如图(蓝色的为起点,白色为终点):
AC代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define N 100005
#define INF 10000000000005ll
typedef long long ll;
struct QuickIO{
QuickIO(){const int SZ = 1<<20;
setvbuf(stdin ,new char[SZ],_IOFBF,SZ);
setvbuf(stdout,new char[SZ],_IOFBF,SZ);
}
}QIO;
struct edge
{
ll to,next,w;
edge(){}
edge(ll to,ll next,ll w)
{
this->to=to;
this->next=next;
this->w=w;
}
}ed[N*2];
ll head[N],lnum,ans,n,k,m;
ll dist[N],mark[N],s[N];
queue<ll>que;
void init()
{
memset(head,-1,sizeof(head));
lnum=0;
ans=INF;
}
void addline(ll a,ll b,ll c)
{
ed[lnum]=edge(b,head[a],c);
head[a]=lnum++;
}
void spfa(ll len,ll flag)
{
if(len==1)return ;
for(ll i=1;i<=n;i++)dist[i]=INF,mark[i]=0;
for(ll i=0;i<k;i+=len)
{
ll ma=i+len;
for(ll j=i;j<min(i+len,k);j++)
{
if(!flag&&j<=(i+ma-1)/2)que.push(s[j]),dist[s[j]]=0,mark[s[j]]=1;
if(flag&&j>(i+ma-1)/2)que.push(s[j]),dist[s[j]]=0,mark[s[j]]=1;
}
}
while(!que.empty())
{
ll k=que.front();
que.pop();
mark[k]=0;
for(ll i=head[k];~i;i=ed[i].next)
{
ll y=ed[i].to;
if(dist[k]+ed[i].w<dist[y])
{
dist[y]=dist[k]+ed[i].w;
if(!mark[y])
{
que.push(y);
mark[y]=1;
}
}
}
}
for(ll i=0;i<k;i+=len)
{
ll ma=i+len;
for(ll j=i;j<min(i+len,k);j++)
{
if(!flag&&j>(i+ma-1)/2)ans=min(ans,dist[s[j]]);
if(flag&&j<=(i+ma-1)/2)ans=min(ans,dist[s[j]]);
}
}
spfa(len/2,flag);
}
int main()
{
ll T;
scanf("%lld",&T);
ll cas=1;
while(T--)
{
init();
scanf("%lld%lld",&n,&m);
for(ll i=0;i<m;i++)
{
ll u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
addline(u,v,w);
}
scanf("%lld",&k);
ll len=1;
while(len<=k)len*=2;
for(ll i=0;i<k;i++)
scanf("%lld",&s[i]);
spfa(len,0);
spfa(len,1);
printf("Case #%lld: %lld\n",cas++,ans);
}
return 0;
}

本文介绍了一种使用SPFA算法求解特定点集中两两间最短路径的方法,并提供了一个具体的实现案例。该算法适用于有向图,通过不断分割点集来减少计算复杂度。

387

被折叠的 条评论
为什么被折叠?



