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

87 篇文章 0 订阅

Senior Pan

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


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
 

Source
 

Recommend
liuyiding
 

题目大意:

    有一张有向图,给你一个点集,问点集之间的任意两点的距离的最小值。


解题思路:

    由于点集可以非常大,直接枚举显然会超时。可以发现,如果把点集划分成两个子集,我们可以使用一次dijkstra求出两个子集间的最短路。如果我们根据点的编号的二进制的每一位,根据这一位为0还是1可以划分子集,就可以把任意两点至少一次划分到两个子集中。所以最终答案一定就是每次dijkstra求得的最短距离的最小值。


AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXN=100000+3;

struct Edge//边
{
    int to, cost;
    Edge(int t, int c):to(t), cost(c){}
};

struct Node//候选构成最短路的点
{
    int u;
    LL c;
    Node(int u, LL c):u(u), c(c){}
    bool operator < (const Node &other)const
    {
        return c>other.c;
    }
};

int N, M, K;
vector<Edge> G[MAXN];//图的邻接表表示
vector<int> save;//询问的点集
LL ans;
bool vis[MAXN];
LL dist[MAXN];

void init()
{
    for(int i=0;i<=N;++i)
        G[i].clear();
    save.clear();
    ans=INF;
}

void dij(int p, int x)//根据编号第p位划分,第p位为x的作起点
{
    for(int i=1;i<=N;++i)//当前位为x,则作为起点
    {
        vis[i]=false;
        dist[i]=INF;
    }
    priority_queue<Node> que;
    for(int i=0;i<save.size();++i)
        if(((save[i]>>p)&1)==x)
        {
            dist[save[i]]=0;
            que.push(Node(save[i], 0));
        }
    while(!que.empty())
    {
        Node tmp=que.top(); que.pop();
        int u=tmp.u;
        if(vis[u])
            continue;
        vis[u]=true;
        for(int i=0;i<G[u].size();++i)
        {
            int v=G[u][i].to;
            if(!vis[v] && dist[v]>dist[u]+G[u][i].cost)
            {
                dist[v]=dist[u]+G[u][i].cost;
                que.push(Node(v, dist[v]));
            }
        }
    }
    for(int i=0;i<save.size();++i)//当前位不为x,则作为终点
        if(((save[i]>>p)&1)!=x)
            ans=min(ans, dist[save[i]]);
}

int main()
{
    int T_T;
    scanf("%d", &T_T);
    for(int cas=1;cas<=T_T;++cas)
    {
        scanf("%d%d", &N, &M);
        init();
        for(int i=0;i<M;++i)
        {
            int u, v, c;
            scanf("%d%d%d", &u, &v, &c);
            G[u].push_back(Edge(v, c));
        }
        scanf("%d", &K);
        for(int i=0;i<K;++i)
        {
            int tmp;
            scanf("%d", &tmp);
            save.push_back(tmp);
        }
        for(int p=0;p<=16;++p)//根据第p位划分,编号最大为1e5,二进制有16位
        {
            dij(p, 0);//由于是有向图,两个集合分别作起点终点的情况
            dij(p, 1);//似乎数据比较水,不枚举这种情况也能过
        }
        printf("Case #%d: %lld\n", cas, ans);
    }
    
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值