Akatsuki

愿有人可与你语海语冰。

HDU 6166-Senior Pan(详解!最短路+二进制优化划分集合)

Senior Pan fails in his discrete math exam again. So he asks Master ZKC to give him graph theory problems everyday.
The task is simple : ZKC will give Pan a directed graph every time, and selects some nodes from that graph, you can calculate the minimum distance of every pair of nodes chosen in these nodes and now ZKC only cares about the minimum among them. That is still too hard for poor Pan, so he asks you for help.
Input
The first line contains one integer T, represents the number of Test Cases.1≤T≤5.Then T Test Cases, for each Test Cases, the first line contains two integers n,m representing the number of nodes and the number of edges.1≤n,m≤100000
Then m lines follow. Each line contains three integers xi,yi representing an edge, and vi representing its length.1≤xi,yi≤n,1≤vi≤100000
Then one line contains one integer K, the number of nodes that Master Dong selects out.1≤K≤n
The following line contains K unique integers ai, the nodes that Master Dong selects out.1≤ai≤n,ai!=aj
Output
For every Test Case, output one integer: the answer
Sample Input
1
5 6
1 2 1
2 3 3
3 1 3
2 5 1
2 4 2
4 3 1
3
1 3 5
Sample Output
Case #1: 2

给一个有向图,然后给出k个点,求这些点里任意两点间的最短路中最短的一条。

思路:对于最短路,可以用Dijkstra或者SPFA算法来实现,这个简单。

问题是如何去遍历这些点,找到任意两点间最短的那一条。

首先要明白,在用Dijkstra或者SPFA算法求解最短路时,不但可以求出任意一点到源点的最短路,它还可以被用来求解一个集合到另一个集合间的最短路。
(集合间的最短路的定义:对于其中一个集合的任一点到另一个集合中的任一点中的最短路径中最短的一条。)

如何求?
很简单,只需要把一个集合里所有的点的dis数组初值赋为0。这样集合里的任意一点到其它点的距离都为0,等价于把集合里所有点认为是一个点x,这样最后求出来的dis数组里存的就是另一个集合里的每一个点到点x的最短路,再找出dis数组里最小的路径,即为集合间的最短路。

现在我们知道了如何求集合间的最短路,那么问题就变成了把k个点划分成两个集合,然后求集合间的最短路。可是每次划分集合,要保证任意两个点之间的最短路都能求到,这样时间复杂度又是巨大的未知数。

下面就是这个问题最关键的地方:二进制划分集合

对于每一组数,按照他们的二进制从低位到高位划分,把当前位上为1的数放到一个集合里,为0的放到另一个集合。这样就保证了划分之后,任意两个点都会匹配到,当然可能会重复,不过在大问题里,这种小问题可忽略不计,不影响总体的时间复杂度。

数据最大为1e5,在经过这样的划分之后,最多划分17次就能求解出答案。

在划分完集合之后,问题就变得很简单,只需要每次划分集合后,一个集合作为起点另一个集合作为终点,跑一遍SPFA,然后反过来再跑一遍SPFA。每次找到最小值即为答案。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
#include<map>
#include<stack>
#include<vector>
#include<queue>
#include<algorithm>
#define max(a,b)   (a>b?a:b)
#define min(a,b)   (a<b?a:b)
#define swap(a,b)  (a=a+b,b=a-b,a=a-b)
#define memset(a,v)  memset(a,v,sizeof(a))
#define X (sqrt(5)+1)/2.0  //Wythoff
#define Pi acos(-1)
#define e  2.718281828459045
using namespace std;
typedef long long int LL;
const int MAXL(1e5);
const int INF(0x3f3f3f3f);
const int mod(1e9+7);
int dir[4][2]= {{-1,0},{1,0},{0,1},{0,-1}};
LL dis[MAXL+50];
int n,m,k;
int a[MAXL+50];
int haxi[MAXL+50];
bool vis[MAXL+50];
vector<pair<int,int> >v[MAXL+50];//邻接表存储图的信息
pair<int,int>p;
LL SPFA()
{
    queue<int>q;
    memset(vis,false);
    memset(dis,INF);
    for(int i=1;i<=k;i++)
    {
        if(haxi[i])//把标记为1的集合设为起点
        {
            q.push(a[i]);
            dis[a[i]]=0;
            vis[a[i]]=true;
        }
    }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=false;
        int len=v[u].size();
        for(int i=0;i<len;i++)
        {
            int ed=v[u][i].first;
            int power=v[u][i].second;
            if(dis[u]+power<dis[ed])
            {
                dis[ed]=dis[u]+power;
                if(!vis[ed])
                {
                    q.push(ed);
                    vis[ed]=true;
                }
            }
        }
    }
    LL ans=INF;
    for(int i=1;i<=k;i++)
    {
        if(!haxi[i])
            ans=min(ans,dis[a[i]]);
    }
    return ans;
}
LL solve()
{
    sort(a+1,a+k+1);
    int ma=a[k];
    int len=0;
    while(ma)//找到k个点里最大值的二进制位数
        len++,ma>>=1;
    LL ans=INF;
    for(int i=0;i<len;i++)
    {
        for(int j=1;j<=k;j++)
        {
            if((a[j]>>i)&1)//将当前位为1的标记为1
                haxi[j]=1;
            else//将当前位为0的标记为0
                haxi[j]=0;
        }
        ans=min(ans,SPFA());//第一次SPFA
        for(int j=1;j<=k;j++)
            haxi[j]=!haxi[j];
        ans=min(ans,SPFA());//把集合互换起点终点重新SPFA
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    int CASE=1;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            p=make_pair(y,z);
            v[x].push_back(p);//有向图
        }
        scanf("%d",&k);
        for(int i=1;i<=k;i++)
            scanf("%d",a+i);
        LL ans=solve();
        cout<<"Case #"<<CASE++<<": "<<ans<<endl;
        for(int i=0;i<=n;i++)//每次清空邻接表
            v[i].clear();
    }
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Akatsuki__Itachi/article/details/79965636
个人分类: 图论
上一篇CodeForces - 960B- Minimize the error(思维--优先队列)
下一篇51Nod-1873 初中的算术(Java-大数浮点型BigDecimal)
想对作者说点什么? 我来说一句

最短路径 贪心实现代码

2009年09月06日 2KB 下载

集合划分问题 c++实现

2008年11月13日 395B 下载

动态规划集合划分

2016年12月08日 15KB 下载

lingo最短路问题代码

2014年07月15日 4KB 下载

ip详解划分 ip详解划分

2010年08月05日 4.09MB 下载

HDU图论题目分类

2013年04月18日 211KB 下载

子网掩码的计算与划分详解

2008年09月11日 9KB 下载

动态规划背包问题入门

2011年07月07日 328KB 下载

没有更多推荐了,返回首页

关闭
关闭