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

14人阅读 评论(0) 收藏 举报
分类:

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();
    }
}
查看评论

HDU 6166 集合间求最短路

题目 Senior Pan Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/O...
  • baidu_36227831
  • baidu_36227831
  • 2017年08月23日 16:57
  • 190

【二进制划分集合 最短路】HDU6166 Senior Pan

想通了就很神奇k个点,可以分为2个集合s1,s2 显然dijkstra一遍即可得到s1到s2的最小距离考虑枚举2进制的每一位 若第i位=1,则分进s1,否则分进s2 显然所有不同的pair...
  • Starry_starry_night
  • Starry_starry_night
  • 2017年08月22日 22:18
  • 176

hdu2844 Coins(多重背包+二进制优化)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2844 题目大意: 有n种价值不同的硬币,他们的数量有限但不相同。现在要求出硬币组合,可组合出多少种...
  • aaaaacmer
  • aaaaacmer
  • 2015年09月18日 14:10
  • 834

动态规划:HDU2844-Coins(多重背包的二进制优化)

Coins Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Sub...
  • yopilipala
  • yopilipala
  • 2017年07月10日 16:48
  • 391

hdu1059 Dividing(多重背包+二进制优化)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1059 题目大意: 有价值分别为1~6的弹珠,不同价值的弹珠有不同的数量。两个人要分成相同的价值,问...
  • aaaaacmer
  • aaaaacmer
  • 2015年09月18日 11:05
  • 736

HDU 6166 Senior Pan(二进制划分集合)

Senior Pan Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) To...
  • Yasola
  • Yasola
  • 2017年08月22日 20:26
  • 497

hdu oj 2544 最短路(最短路径)

最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Subm...
  • u014253173
  • u014253173
  • 2014年08月10日 15:16
  • 1982

hdu 6166 Senior Pan 最短路

Senior Pan Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) To...
  • overload1997
  • overload1997
  • 2017年08月22日 20:32
  • 898

HDU - 2433 Travel (最短路树)

题目大意:有N个点,M条边,每次删掉一条边,问删掉该边后,所有点之间的最短路的和是多少解题思路:刚开始就想,删掉一次floyd一次,结果可想而之,TLE了 后来看了别人的,发现了一种叫做最短路树的东...
  • L123012013048
  • L123012013048
  • 2015年07月31日 23:57
  • 1665

HDU-2544 最短路【最短路】

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2544 最近复习了最短路径的算法,就写了4个版本的测试。正好是模板题,就果断A之。。。 Di...
  • niushuai666
  • niushuai666
  • 2012年02月25日 09:31
  • 2437
    个人资料
    持之以恒
    等级:
    访问量: 1万+
    积分: 1458
    排名: 3万+
    最新评论