hdu-6166 Senior Pan

针对大规模图中的最短路径问题,介绍了一种通过将节点集分成A、B两组,并利用超级源点与超级汇点优化Dijkstra算法的方法,有效减少计算复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
2017 Multi-University Training Contest - Team 9

题意:n个点, m条边, k个点的集合, 求这个集合中所有点对的最短路的最小值。
题解:nm都在1e5, 暴力枚举肯定超时, 可以把k个点分为两个集合, A集合和B集合, A集合的点与超级源点相连, B集合的点与超级汇点相连,距离为0, 跑一边最短路就是A点到B点的最短路的最小值了。然后可以枚举每一位的二进制,为0就放入A, 为1就放入B, 因为每个点不相同, 所以每对点都会至少一次放入不同的AB两个集合, 最多只用跑16遍最短路就好。
还有一种思路是随机化, 随机每个点在哪个集合, 因为答案只可能是两个点产生的, 所以最短的那条路的起点和终点都是固定的,每次随机,起点在正确的集合的概率为1/2,终点在正确的集合的概率为1/2,每次随机的概率即为1/4, 只用随机20次左右,每次取最小的就是答案。错误的概率就为(3/4)^20 ≈ 0.003 相信大家的人品不会差到交一次会wa的地步吧哈哈哈。

写的比较丑, 所以时间很长。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int MaxN = 100000;

struct node
{
    int v, w, next;
}e[MaxN * 10 + 1];

struct Node
{
    long long d;
    int x;
    bool operator < (const Node p) const
    {
        return d > p.d;
    }
};

int g[MaxN + 1], cnt;
int n, m;
int uu[MaxN + 1], vv[MaxN + 1], ww[MaxN + 1];
int a[MaxN + 1];

void addedge(int u, int v, int w)
{
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = g[u];
    g[u] = cnt++;
}

long long dijkstra()
{
    long long dist[MaxN + 1];
    for (int i = 1; i <= n + 1; i++)
        dist[i] = 1e12;
    dist[0] = 0;
    priority_queue<Node> q;
    Node t;
    t.x = 0, t.d = 0;
    q.push(t);

    while (!q.empty())
    {
        Node cur = q.top();
        q.pop();
        for (int i = g[cur.x]; i != -1; i = e[i].next)
        {
            int v = e[i].v;
            if (dist[v] > cur.d + e[i].w)
            {
                dist[v] = cur.d + e[i].w;
                Node t;
                t.x = v;
                t.d = dist[v];
                q.push(t);
            }
        }
    }
    return dist[n + 1];
}

int main()
{
    int T, cas = 0;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d %d", &n, &m);
        for (int i = 1; i <= m; i++)
            scanf("%d %d %d", &uu[i], &vv[i], &ww[i]);
        int Q;
        scanf("%d", &Q);
        for (int i = 1; i <= Q; i++)
            scanf("%d", &a[i]);
        int S = 0, T = n + 1;
        long long ans = 1e12;
        for (int j = 0; j <= 16; j++)
        {
            memset(g, -1, sizeof(g));
            cnt = 0;
            for (int i = 1; i <= m; i++)
                addedge(uu[i], vv[i], ww[i]);
            for (int i = 1; i <= Q; i++)
            {
                if (a[i] & (1 << j))
                    addedge(S, a[i], 0);
                else
                    addedge(a[i], T, 0);
            }
            ans = min(dijkstra(), ans);
            memset(g, -1, sizeof(g));
            cnt = 0;
            for (int i = 1; i <= m; i++)
                addedge(uu[i], vv[i], ww[i]);
            for (int i = 1; i <= Q; i++)
            {
                if (a[i] & (1 << j))
                    addedge(a[i], T, 0);
                else
                    addedge(S, a[i], 0);
            }
            ans = min(dijkstra(), ans);
        }
        printf("Case #%d: %lld\n", ++cas, ans);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值