HDU 6166 集合间求最短路

题目

Senior Pan

Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 691    Accepted Submission(s): 263


Problem Description
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

题目大意


   求给出的点之间形成的点对里面的最短路


解题思路

   要是一遍一遍的去跑最短路,一定会超时,这里用的是求两个集合间的最短路来实现,把给我们的点集分成两个集合,这里用的是二进制来判断的,通过判断该数字是否在某一位上是不是1,是1加入一个集合,是0加入另一个集合,因为m<100000,化成二进制最多20位(估算的,没准确计算,1024*1024,1024是十位),这样最多20次,就一定可以保证每两个点都分在了不同的两个集合,然后求两个集合的最短路就可以了

  求两个集合的最短路只要开两个超源就可以了,开一个0是一个集合的元素到0都为0,开一个超源n+1是另一个集合到n+1的距离都为0,这样子求0-n+1的最短路就是求两个集合之间的最短路径了


代码

#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3fLL
const int maxn=100000+10;
int x[maxn] , y[maxn] , val[maxn] , a[maxn] , g[maxn] , vis[maxn] ;
LL d[maxn] ;
int case_=1 , cnt ;
struct node
{
    int x ;
    LL v ;
    bool operator < ( const node &b ) const ///优先队列改写小顶堆
    {
        return v > b.v ;
    }
};
struct edge
{
    int x ;
    int to ;
    LL  v ;
}e[maxn<<1];

void addedge(int u,int v,int val)
{
    e[++cnt] = (edge){v , g[u] , val} ;
    g[u] = cnt ;///用链式前向星来记录
}
LL dijk(int u, int v)
{
    priority_queueq;
    for(int i = 0 ; i <= v ;i ++)
        d[i] = INF , vis[i] = 0 ;
    d[u] = 0 ;
    //node tmp = (node){ u , 0 } ;
    q.push((node){ u , 0 }) ;
    while(!q.empty())
    {
        node t = q.top() ;
        q.pop() ;
        if(vis[t.x])
            continue;
        vis[t.x] = 1;
        for(int i = g[t.x] ; ~i ; i = e[i].to)///~i      i!=-1
        {
            if(!vis[e[i].x] && d[e[i].x] > d[t.x] + e[i].v)
            {
                //vis[e[i].x]=1;
                d[e[i].x] = d[t.x] + e[i].v ;
                q.push((node){e[i].x , d[e[i].x]}) ;
            }
        }
        //vis[t.x]=0;
    }
    return d[v] ;
}
int main()
{
    int T ;
    scanf("%d", &T) ;
    while(T--)
    {
        int n , m , k ;
        LL ans = INF ;
        scanf("%d%d" , &n , &m) ;
        for(int i = 1 ; i <= m ; i++ )  scanf("%d%d%d",&x[i] , &y[i] , &val[i]) ;
        scanf("%d" , &k) ;
        for(int i = 1 ; i <= k ; i++)   scanf("%d" , &a[i]) ;
        for(int t = 0 ; t < 20 ; t++)
        {
              cnt = 0 ;
              memset(g , -1 , sizeof(g)) ;
              for(int i = 1 ; i <= m ; i++)
             {
                 addedge(x[i] , y[i] , val[i]);
             }
              for(int i = 1 ; i <= k ; i++)
             {
                  if(a[i] & (1 << t))///分成两个集合,一个集合开一个超源0,使该集合到0距离为0
                    addedge(0 , a[i] , 0) ;
                  else               ///另一个集合开一个超源n+1,是该集合到n+1的距离为0,这样就可以求两个集合间的最短路径了
                    addedge(a[i] , n + 1 ,0) ;
             }
              ans = min(ans , dijk(0 , n + 1)) ;
           //   cout << "ans : " << ans << endl ;
                cnt = 0 ;            ///两个集合交换位置
               memset(g , -1 , sizeof(g)) ;
              for(int i = 1 ; i <= m ; i++)
             {
                 addedge(x[i] , y[i] , val[i]) ;
             }
              for(int i = 1 ; i <= k ; i++)
             {
                  if((a[i] & (1 << t)) == 0)///逻辑且优先级低,要加括号
                    addedge(0 , a[i] , 0) ;
                  else
                    addedge(a[i] , n + 1 ,0) ;
             }
              ans = min(ans , dijk(0 , n+1)) ;
        //    cout << "ans : " << ans << endl ;
        }
        printf("Case #%d: %lld\n",case_++ , ans) ;
    }
    return 0;
}


你可能感兴趣的:(HDU,最短路,HDU,最短路)