1. 程式人生 > 實用技巧 >2015ACM/ICPC亞洲區瀋陽站-重現賽 M - Meeting (特殊建邊,最短路)

2015ACM/ICPC亞洲區瀋陽站-重現賽 M - Meeting (特殊建邊,最短路)

  • 題意:有\(n\)個點,\(m\)個集合,集合\(E_i\)中的點都與集合中的其它點有一條邊權為\(t_i\)的邊,現在問第\(1\)個點和第\(n\)個點到某個點的路徑最短,輸出最短路徑和目標點,如果不滿足條件則輸出\(Evil John\).

  • 題解:題目所給的邊數關係太複雜了,我們可以讓每個集合中的所有點都與一個虛擬節點連邊,而這些點兩兩卻不連,然後再去找\(1\)個和第\(n\)個點的最短路徑,不難發現,最終得到的路徑為\(dis[i]/2\),所以我們只要用虛擬節點建邊然後跑兩次dijkstra,最後判斷輸出一下即可.

  • 程式碼:

    struct misaka{
        ll val;
        ll out;
    }e;
    
    ll t;
    ll n,m;
    ll u;
    ll cost,E;
    vector<misaka> v[N];
    ll dis[2][N];
    bool st[N];
    vector<ll> ans;
    
    void dijkstra(ll start,int op){
        me(st,false,sizeof(st));
        for(int i=0;i<N;++i) dis[op][i]=INF;
        dis[op][start]=0;
    
        priority_queue<PLL,vector<PLL>,greater<PLL>> h;
        h.push({0,start});
    
        while(!h.empty()){
            auto tmp=h.top();
            h.pop();
    
            ll num=tmp.se;
            ll dist=tmp.fi;
            if(st[num]) continue;
            st[num]=true;
    
            for(auto w:v[num]){
                ll j=w.out;
                if(dis[op][j]>dist+w.val){
                    dis[op][j]=dist+w.val;
                    h.push({dis[op][j],j});
                }
            }
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        cin>>t;
        int p=1;
        while(t--){
            for(ll i=0;i<N;++i){
                v[i].clear();
            }
        	cin>>n>>m;
        	for(ll i=1;i<=m;++i){
                cin>>cost;
                cin>>E;
                for(ll j=1;j<=E;++j){
                    cin>>u;
                    e.out=n+i;
                    e.val=cost;
                    v[u].pb(e);
                    e.out=u;
                    v[n+i].pb(e);
                }
            }
                dijkstra(1,0);
                dijkstra(n,1);
    
                ll res=INF;
                ans.clear();    
                for(ll i=1;i<=n;++i){
                    res=min(res,max(dis[0][i],dis[1][i]));
                }
                if(res>=INF){
                    cout<<"Case #"<<p<<": Evil John"<<endl;
                }
                else{
                    cout<<"Case #"<<p<<": "<<res/2<<endl;
                    for(ll i=1;i<=n;++i){
                        if(max(dis[0][i],dis[1][i])==res){
                          ans.pb(i);
                         }
                     }
                    for(int i=0;i<(int)ans.size();++i){
                        cout<<ans[i];
                        if(i!=(int)ans.size()-1) cout<<" ";
                    }
                    cout<<'\n';
                }
                p++;
        }
    
        return 0;
    }