文章標題 HDU 6166 : Senior Pan(二進制+dijkstra)

Senior Pan

題目鏈接
題意:有n個點,m條邊的有向圖,然後從中拿出k個點,然後要我們求出這k個點中距離最短的兩個點。
分析:巧妙的用了二進制的思想,對於一個點上的一個二進制位,我們可以知道,不是1就是0,那麼我們就可以通過判斷這個二進制位是否是1來將這k個點的分成兩個集合,由於n的大小隻有1e5,所以最多隻需要枚舉20個二進制爲, 然後就是當把k個點分成兩個集合後,就是求兩個集合之間的最短距離了,求兩個集合之間的最短距離就可以用dijkstra來求,其實跟求點到點的最短距離差不多,區別就是一開始先將一個集合的點先放進優先隊列,然後當出隊的是另一個集合的點,說明最短距離找到了。
代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
typedef long long ll;

const int mod=1e9+7;
const int maxn=1e5+10;
const long long inf=1e17;

int n,m,k;
int num[maxn];
int mark[maxn],vis[maxn];

ll dis[maxn];
struct Edge{
    int to,nex,w;
}edge[maxn];

struct node {
    int u;
    ll dis;
    bool operator <(const node &t)const {
        return dis>t.dis;
    }
};
priority_queue<node>q;
int tot,head[maxn];

void addedge(int u,int v,int w){
    edge[tot]=Edge{v,head[u],w};
    head[u]=tot++;
} 
void init(){
    memset (mark,0,sizeof (mark));
    memset (vis,0,sizeof (vis));
    for (int i=0;i<=n;i++)dis[i]=inf;
    while (!q.empty())q.pop();
}
ll dijkstra(){
    while (!q.empty()){
        node tmp=q.top();q.pop();
        int u=tmp.u;
        if (mark[u])return tmp.dis;//如果遇到背標記的一個點,那麼就是到達另一個集合的最短距離了 
        if (vis[u])continue;
        vis[u]=true;
        for (int i=head[u];i!=-1;i=edge[i].nex){
            int v=edge[i].to;
            int cost=edge[i].w;
            if (!vis[v]&&dis[v]>dis[u]+cost){
                dis[v]=dis[u]+cost;
                q.push(node{v,dis[v]});
            }
        }
    }
    return inf;
}

ll solve(){
    ll ans=inf;
    for (int i=0;i<20;i++){//通過二進制將k個數的集合分成兩個集合,求集合到集合的最短距離 
        init();
        for (int j=0;j<k;j++){
            if (num[j]&(1<<i)){
                mark[num[j]]=1;
            }else {
                dis[num[j]]=0;
                q.push(node{num[j],0});
            }
        }
        ans=min(ans,dijkstra()); 
        init();
        for(int j=0;j<k;j++){//由於是有向圖,所以得分兩種情況 
            if (num[j]&(1<<i)){
                q.push(node{num[j],0});
            }
            else {
                mark[num[j]]=1;
            }
        }
        ans=min(ans,dijkstra());
    }
    return ans;
}

int main()
{
    int T;
    scanf ("%d",&T);
    int cas=1;
    while (T--){
        scanf ("%d%d",&n,&m);
        tot=0;
        memset (head,-1,sizeof (head));
        int u,v,w;
        for (int i=0;i<m;i++){
            scanf ("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        scanf ("%d",&k);
        for (int i=0;i<k;i++){
            scanf ("%d",&num[i]);
        }
        printf ("Case #%d: %lld\n",cas++,solve());
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章