HDU 6166 Senior Pan [二分+SPFA]

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

题意:给出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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值