1. 程式人生 > 實用技巧 >*牛妹遊歷城市【最短路建虛點】

*牛妹遊歷城市【最短路建虛點】

題意

題目連結:https://ac.nowcoder.com/acm/contest/6885/E

分析

如果直接建邊,肯定會超時。那麼,就要進行轉化。可以設定 \(32\) 個虛點,分別表示點權的第 \(i\) 位為 \(1\)。對於點權 \(a[i]\) 如果其第 \(j\) 位為 \(1\),那麼就從該點連一條權值為 \(2^j\) 的邊到對應的虛點,並建立一條權值為 \(0\) 的反向邊。最後跑一遍最短路即可。

證明:對於一個點,按照最短路的貪心策略,必然會選擇與之相連的權值最小的出邊,這樣剛好滿足了題目建邊的原則。並且,通過虛邊,可以使得兩點是按 \(lowbit\) 建邊。

程式碼

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<ll,int>pli;
const int N=1e5+100;
const ll inf=1e18;
ll dis[N];
int n;
vector<pli>pic[N];
priority_queue<pli,vector<pli>,greater<pli> > que;
void addedge(int u,ll w)
{
    for(int i=0;i<32;i++)
    {
        if((w>>i)&1)
        {
            pic[u].pb(make_pair((1LL<<i),i));
            pic[i].pb(make_pair(0,u));
        }
    }
}
void dij()
{
    for(int i=0;i<=n+32;i++)
        dis[i]=inf;
    while(!que.empty()) que.pop();
    dis[33]=0;
    que.push(make_pair(0,33));
    while(!que.empty())
    {
        pli now=que.top();
        que.pop();
        if(dis[now.second]<now.first) continue;
        for(int i=0;i<pic[now.second].size();i++)
        {
            pli tmp=pic[now.second][i];
            if(dis[tmp.second]>dis[now.second]+tmp.first)
            {
                dis[tmp.second]=dis[now.second]+tmp.first;
                que.push(make_pair(dis[tmp.second],tmp.second));
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll a;
        scanf("%d",&n);
        for(int i=0;i<=n+32;i++)
            pic[i].clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a);
            addedge(i+32,a);
        }
        dij();
        if(dis[n+32]==inf) printf("Impossible\n");
        else printf("%lld\n",dis[n+32]);
    }
    return 0;
}

題解:https://blog.nowcoder.net/n/13d05ab8ac22444a81fbe475de2f563e