HDU 6166 Senior Pan 2017多校第9场

  • 题意:

    • 给定一个带权有向图,一个点集(k个点,无相同点)
    • 求点集元素间的最短距离
  • 规模:

    • 1<=n,m,k<=1e5
    • 1<=w<=1e5
  • 类型:

    • 图论,最短路
    • 二进制优化
  • 分析:

    • 考虑最暴力的做法,枚举点集里所有点,求单源最短路,复杂度k*nlogm,当然是过不了
    • 这里我们优化求最短路的次数
    • 纯暴力的做法很容易看出来有多余的计算

    • 全集合最短路有两个端点,分别位于两个点集S,T。

    • 如果我们能将集合内任意两点,分割到两个点集里,求最短路,那么一定包含全集合的最短路
    • 减少分割次数就能优化掉k
    • 任意两点的序号的二进制不同,一定有一位二进制位能将两点划分到两个集合
    • 枚举二进制位来划分最多20次
  • 时间复杂度&&优化:

    • O(20*nlogm)
  • 代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
const int MAXM=1e5+10;
const int INF=0x3fff3fff;


int n,m,k;
int kk[MAXN];
int dist[MAXN];

struct Edge{
    int v,w;
};
vector<Edge> edge[MAXN];
void addedge(int u,int v,int w){
    Edge x;x.v=v;x.w=w;
    edge[u].push_back(x);
}

struct node{
    int x;
    int dis;
    node(int _x=0,int _dis=0):x(_x),dis(_dis){}
    bool operator < (const node &other)const{
        if(dis!=other.dis)return dis>other.dis;
        return x-other.x;
    }
};

priority_queue<node> q;
int vis[MAXN];

int dijstra(){
    int res;
    while(!q.empty()){
        node now=q.top();
        q.pop();
        int u=now.x;
        int dis=now.dis;
        if(vis[u])return dis;
        //vis[u]=1;  //多了这行会wa,毕竟不是单源最短路,目标点也是固定的点集
        for(int i=0;i<edge[u].size();i++){
            int v=edge[u][i].v;
            int w=edge[u][i].w;
            if(dist[v]>dist[u]+w){
                dist[v]=dist[u]+w;
                q.push(node(v,dist[v]));
            }
        }
    }
    return INF;
}

void init(){
    for(int i=0;i<MAXN;i++)dist[i]=INF;
    memset(vis,0,sizeof(vis));
    while(!q.empty())q.pop();

}

int solve(){
    int res=INF;
    for(int i=0;i<20;i++){
        init();
        for(int j=0;j<k;j++){
            int v=kk[j];
            if(v&(1<<i)){
                q.push(node(v,0));
                dist[v]=0;
            }
            else {
                vis[v]=1;
            }
        }
        res=min(res,dijstra());
        init();
        for(int j=0;j<k;j++){
            int v=kk[j];
            if(!( v&(1<<i) )){
                q.push(node(v,0));
                dist[v]=0;
            }
            else {
                vis[v]=1;
            }
        }
        res=min(res,dijstra());

    }
    return res;
}

int main()
{
    int T;
    cin>>T;
    int kase=0;
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<MAXN;i++)edge[i].clear();
        for(int i=0;i<m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        scanf("%d",&k);
        for(int i=0;i<k;i++)scanf("%d",&kk[i]);
        printf("Case #%d: %d\n", ++kase,solve());
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值