1. 程式人生 > >HDU-5521 Meeting(最短路)

HDU-5521 Meeting(最短路)

HDU-5521 Meeting (最短路)

題目大意

給標記從1-n的節點,並且進行了分塊(分塊可能會有重合),第i個分塊內部有sis_i個節點,節點兩兩之間都有路徑相連,通過的時間都是tit_i。現在一頭奶牛從節點1出發,另一頭從n出發,想找一箇中間的節點開會,想找一個節點使得路上花費的時間最小。

思路

很顯然一個最短路的問題。從1點跑一個最短路,從n點跑一個最短路,每一個節點都算一個開會時間,然後取最短就行。而一個節點的開會時間一定是兩頭牛中來得慢的那頭的路上花費時間(同時出發,來得早的可以等他的同伴)。問題其實有兩個。

首先是建圖的問題。如果用普通的鄰接表或者鄰接矩陣抑或是鏈式前向星,分塊都是完全圖/團,所以建圖的時間是O

(n2)O(n^2),肯定要T。所以我隊隊寶阿忠哥想出來一個“阿忠圖”的建圖方式,就是把所有的團內部的節點放進一個集合,並把每個集合標記上自己所在的團的編號,每次最短路鬆弛的時候就遍歷所有自己有在的團,具體就是:

vector<long long> es[1000005];//標記每個團內有哪些節點
vector<long long> inque[100005];//標記每個節點在哪些團內
/*
*以下省略N行程式碼
*/
	for(long long i=1; i<=m; i++)
    {
        es[i].clear();//清空第i個團
        long long s;
        scanf("%lld%lld",&t[i],&s);
        for(long long j=0; j<s; j++)
        {
            long long temp;
            scanf("%lld",&temp);
            inque[temp].push_back(i);//標記temp節點所在的團有i
            es[i].push_back(temp);//標記i團內有temp節點
        }
    }

建圖時間就是O(n)O(n)

第二個問題就是因為團內節點兩兩之間都有相同權值的邊,所以團內節點鬆弛沒有意義,所以每次Dijkstra,每個團都只需要遍歷一次。

這個地方是最難想的,我們隊在打這場2015 ICPC瀋陽站的重現賽的時候,什麼都想到了,以為我的程式碼寫醜了阿忠哥幫我重寫了,輸入輸出換成scanf和printf了,甚至各種STL都替換成手寫版本了,阿忠哥已經把優先佇列換成手寫小頂堆了,仁至義盡,無力迴天,T了整整(1000)B(1000)_B發!!!(根本不是卡常啊喂!被卡常卡怕了的我隊日常)反正賽後搜了題解,秒懂。。。然後把我的醜陋STL依賴程式碼改了兩行程式碼秒過。

具體實現就是加一個bool型vis陣列,每次遍歷完一個團就標記了,下次不許遍歷這個團了:

		for(long long i=0; i<inque[now.second].size(); i++)
        {
            long long nxtedge=inque[now.second][i];
            if(vis[nxtedge])
            {
                continue;//遍歷過了不許遍歷了
            }
            vis[nxtedge]=true;//該團遍歷過了標記一個
            for(long long j=0; j<es[nxtedge].size(); j++)
            {
                long long nxt=es[nxtedge][j];
                if(nxt!=now.second && dis[nxt]>dis[now.second]+t[nxtedge])
                {
                    dis[nxt]=dis[now.second]+t[nxtedge];
                    qq.push(make_pair(dis[nxt],nxt));
                }
            }
        }

加了這個優化,寫的最醜的依賴STL我的程式碼都過了

程式碼

#include <bits/stdc++.h>
using namespace std;
vector<long long> inque[100005];
vector<long long> es[1000005];
vector<long long> ans;
long long t[1000005];
long long dis[100005];
long long dis2[100005];
long long n,m;
bool vis[1000005];
void dij(long long s)//其實這裡傳一個數組參就可以了,我複製了一份。。。
{
    memset(dis,0x3f,sizeof(dis[0])*(n+1));
    memset(vis,0,sizeof(vis[0])*(n+1));
    dis[s]=0;
    priority_queue<pair<long long,long long>,vector<pair<long long,long long> >,greater<pair<long long,long long> > > qq;
    qq.push(make_pair(dis[s],s));
    while(!qq.empty())
    {
        pair<long long,long long> now = qq.top();
        qq.pop();
        if(now.first>dis[now.second])
        {
            continue;
        }
        for(long long i=0; i<inque[now.second].size(); i++)
        {
            long long nxtedge=inque[now.second][i];
            if(vis[nxtedge])
            {
                continue;
            }
            vis[nxtedge]=true;
            for(long long j=0; j<es[nxtedge].size(); j++)
            {
                long long nxt=es[nxtedge][j];
                if(nxt!=now.second && dis[nxt]>dis[now.second]+t[nxtedge])
                {
                    dis[nxt]=dis[now.second]+t[nxtedge];
                    qq.push(make_pair(dis[nxt],nxt));
                }
            }
        }
    }
}
void dij2(long long s)
{
    memset(dis2,0x3f,sizeof(dis2[0])*(n+1));
    memset(vis,0,sizeof(vis[0])*(n+1));
    dis2[s]=0;
    priority_queue<pair<long long,long long>,vector<pair<long long,long long> >,greater<pair<long long,long long> > > qq;
    qq.push(make_pair(dis2[s],s));
    while(!qq.empty())
    {
        pair<long long,long long> now = qq.top();
        qq.pop();
        if(now.first>dis2[now.second])
        {
            continue;
        }
        for(long long i=0; i<inque[now.second].size(); i++)
        {
            long long nxtedge=inque[now.second][i];
            if(vis[nxtedge])
            {
                continue;
            }
            vis[nxtedge]=true;
            for(long long j=0; j<es[nxtedge].size(); j++)
            {
                long long nxt=es[nxtedge][j];
                if(nxt!=now.second && dis2[nxt]>dis2[now.second]+t[nxtedge])
                {
                    dis2[nxt]=dis2[now.second]+t[nxtedge];
                    qq.push(make_pair(dis2[nxt],nxt));
                }
            }
        }
    }
}
void solve()
{
    scanf("%lld%lld",&n,&m);
    for(long long i=1; i<=n; i++)
    {
        inque[i].clear();
    }
    for(long long i=1; i<=m; i++)
    {
        es[i].clear();
        long long s;
        scanf("%lld%lld",&t[i],&s);
        for(long long j=0; j<s; j++)
        {
            long long temp;
            scanf("%lld",&temp);
            inque[temp].push_back(i);
            es[i].push_back(temp);
        }
    }
    dij(1);
    dij2(n);
    long long miner=0x3f3f3f3f3f3f3f3f;
    for(long long i=1; i<=n; i++)
    {
        if(dis[i]<0x3f3f3f3f3f3f3f3f && dis2[i]<0x3f3f3f3f3f3f3f3f)
        {
            long long newer=max(dis[i],dis2[i]);
            miner=min(miner,newer);
        }
    }
    if(miner!=0x3f3f3f3f3f3f3f3f)
    {
        printf("%lld\n",miner);
        ans.clear();
        for(long long i=1; i<=n; i++)
        {
            if(dis[i]<0x3f3f3f3f3f3f3f3f && dis2[i]<0x3f3f3f3f3f3f3f3f)
            {
                long long newer=max(dis[i],dis2[i]);
                if(newer==miner)
                {
                    ans.push_back(i);
                }
            }
        }
        for(long long i=0; i<ans.size(); i++)
        {
            printf("%lld",ans[i]);
            if(i!=ans.size()-1)
            {
                printf(" ");
            }
            else
            {
                printf("\n");
            }
        }
    }
    else
    {
        printf("Evil John\n");
    }

}
int main()
{
    long long t;
    scanf("%lld",&t);
    for(long long caser=1; caser<=t; caser++)
    {
        printf("Case #%lld: ",caser);
        solve();
    }
    return 0;
}