HDU - 6166 SPFA最短路次短路 or 二进制+Dijstra

题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6166

题意:给一个有向图,给一个集合,从集合内任选两个点的最短路最短为多少。

分析:两种解法,一种是题解中给的,感觉非常巧妙,把集合分为两个集合,改成多源多汇最短路,如何保证任意一对点都分在过不同的集合呢,就是使用二进制,对于每一二进制位,二进制表示下不同的就分在不同的集合,因为对于每一对数,至少有一位二进制不同,所有保证每一对点都被分在过不同的集合了。第二种解法就比较笨,比赛中没有想到题解那么好的解法,就直接使用了SPFA进行松弛操作,但是要防止自己走向自己的情况,就保存了最终父节点不同的最短路和次短路,然后一直松弛到不能松弛为止就行了。

代码:

第一种解法的代码网上很多,也很好写,遍历二进制位,然后对于每一位,使用Dijstra,为0的dis设为0,为1的做个标记,跑的过程中,走到任意一个有标记的就可以return了,写起来很简单,代码就不给出了。

第二种代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f

using namespace std;

const int maxm=200005;
const int maxn=100005;

int m,n;
int edgenum,head[maxn],num[maxn];
long long dis[2][maxn];
int vis[maxn];
int fa[2][maxn];

deque<int> que;

struct node
{
	int to,len,next;
}edge[maxm];

void init(int n)
{
    que.clear();
	edgenum=0;
	for(int i=1;i<=n;i++)
    {
        head[i]=-1;
        dis[0][i]=dis[1][i]=inf;
        fa[0][i]=fa[1][i]=0;
        vis[i]=0;
    }
}

int add(int u,int v,int d)
{
	edge[edgenum]={v,d,head[u]};
	head[u]=edgenum++;
}

int main()
{
    //freopen("1006.in","r",stdin);
    ios::sync_with_stdio(false);
    int T,n,m,u,v,len,k,a[100005];
    cin>>T;
    for(int ca=1;ca<=T;ca++)
    {
        cin>>n>>m;
        init(n);
        for(int i=1;i<=m;i++)
        {
            cin>>u>>v>>len;
            add(u,v,len);
        }
        cin>>k;
        for(int i=1;i<=k;i++)
        {
            cin>>a[i];
            for(int j=head[a[i]];j!=-1;j=edge[j].next)
            {
                int v=edge[j].to;
                if(dis[0][v]>edge[j].len)
                {
                    if(a[i]==fa[1][v])
                    {
                        fa[1][v]=fa[0][v];
                        dis[1][v]=dis[0][v];
                    }
                    if(!vis[v])
                    {
                        vis[v]=1;
                        que.push_back(v);
                    }
                    fa[0][v]=a[i];
                    dis[0][v]=edge[j].len;
                }
                else if(dis[1][v]>edge[j].len&&a[i]!=fa[0][v])
                {
                    if(!vis[v])
                    {
                        vis[v]=1;
                        que.push_back(v);
                    }
                    fa[1][v]=a[i];
                    dis[1][v]=edge[j].len;
                }
            }
        }
        while(!que.empty())
        {
            int u=que.front();
            que.pop_front();
            vis[u]=0;
            for(int i=head[u];i!=-1;i=edge[i].next)
            {
                int v=edge[i].to;
                for(int j=0;j<=1;j++)
                {
                    if(v!=fa[j][u])
                    {
                        if(dis[0][v]>dis[j][u]+edge[i].len)
                        {
                            if(fa[j][u]==fa[1][v])
                            {
                                fa[1][v]=fa[0][v];
                                dis[1][v]=dis[0][v];
                            }
                            if(!vis[v])
                            {
                                vis[v]=1;
                                que.push_back(v);
                            }
                            fa[0][v]=fa[j][u];
                            dis[0][v]=dis[j][u]+edge[i].len;
                        }
                        else if(dis[1][v]>dis[j][u]+edge[i].len&&fa[j][u]!=fa[0][v])
                        {
                            if(!vis[v])
                            {
                                vis[v]=1;
                                que.push_back(v);
                            }
                            fa[1][v]=fa[j][u];
                            dis[1][v]=dis[j][u]+edge[i].len;
                        }
                    }
                }
            }
        }
        long long ans=inf;
        for(int i=1;i<=k;i++)
            ans=min(ans,dis[0][a[i]]);
        cout<<"Case #"<<ca<<": "<<ans<<endl;
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值