hdu 5723 Abandoned country 深搜回溯
阿新 • • 發佈:2018-11-05
題目連結:hdu 5723
參考部落格:https://www.cnblogs.com/aiguona/p/7214707.html
題意:給出n個頂點,m條邊,讓你建一顆最小生成樹,問:最小生成樹的值是多少,任意兩點的期望長度是多少?
題解:
任意兩點的期望是(權值)*(這條路被走過的次數)的總和 除以 總共的路徑數。
比如從A到B距離為2,從B到C距離為3。那麼從A到B走過2一次,從A到C走過2一次,走過3一次,從B到C走過3一次。
所以期望為(2*2+3*2)/ 3 = 3.33。(也可以看成有三條路徑,1,A->B , B->C , A->C 其實B->A也算一種的,但我們可以不用考慮,因為求得是期望值,所以我們就按順著的方向來算路徑)。
總共的路徑數為 n*(n-1)/2 ,舉個例子給你,有4個頂點,那麼就有3個值,那麼首先有3條路徑,分別為 A->B B->C C->D (經過兩個頂點),
然後有2條路徑,分別為 A->C B->D(經過三個頂點) , 最後有1條路徑,為 A->D(經過4個頂點),很容易可以看出是個等差數列和(每次少一條路徑)。
這裡不能求最短路,超時。
應該用深搜回溯,找出這條路被走過過少次。這樣就可以求出期望了。
這裡由於資料量比較大,不能用prim鄰接矩陣求最小生成樹(vector應該可以),可以用Kruskal求最小生成樹。
坑點:注意最後求期望的時候總共的路徑數量n*(n-1)/2資料量比較大,應該用long long或者double儲存。
///程式碼參考部落格:https://www.cnblogs.com/aiguona/p/7214707.html #include<cstdio> #include<algorithm> #include<vector> #include<iostream> #include<string.h> using namespace std; vector<pair<int,int> > v[100010]; struct Edge { int f,t,q; }; int m,n;///n為村莊數,m為街道數 Edge s[1000010];///儲存圖 long long ans;///存最後的每條路的總和 int pre[100010];///並查集的祖先陣列 int vis[100010];///標記陣列 bool cmp(Edge a,Edge b )///排序函式 { return a.q<b.q; } int Find(int x)///找祖先 { if(x!=pre[x]) { pre[x]=Find(pre[x]); } return pre[x]; } void Merge(int x,int y)///查是否相等 { int fx=Find(x); int fy=Find(y); if(fx!=fy) pre[fx]=fy; } long long dfs(int x) ///dfs遞迴搜尋 { vis[x]=1; long long now=0,all=1;///now記錄當前節點直接連線的節點數量 all記錄此節點經過搜尋後所有的與此節點連線的節點數 int h=v[x].size(); for(int i=0; i<h; i++) { int b=v[x][i].first; if(!vis[b]) { now=dfs(b); all+=now; // printf("b=%d,all=%lld,now=%lld\n",b,all,now); ///路徑數為 now*(n-now),解釋下:與x節點連線的後續節點數為now個,那麼與x連線的前置節點就有(n-now)個, ///所有的前置節點都可以和後續節點形成一條順著的路徑 ///故為 now*(n-now)條路徑 ans+=now*(n-now)*v[x][i].second;///ans記錄的是權值*經過的次數 } } return all; } int main() { int t; scanf("%d",&t); while(t--) { ans=0; memset(vis,0,sizeof(vis)); scanf("%d%d",&n,&m); if(m==0||n==0) {printf("0 0.00");continue;} for(int i=0;i<=n;i++) v[i].clear(); for(int i=0; i<=n; i++)///並查集的祖先節點的初始化 { pre[i]=i; } for(int j=0; j<m; j++)///輸入 { scanf("%d%d%d",&s[j].f,&s[j].t,&s[j].q); } sort(s,s+m,cmp);///排序 long long sum=0;///sum用來記錄最小生成樹的長度 for(int j=0; j<m; j++) { int fx=Find(s[j].f); int fy=Find(s[j].t); if(fx!=fy) ///如果祖先不相等,那麼加入到最小生成樹中 { sum=sum+s[j].q; Merge(fx,fy); ///加入到動態陣列中準備做期望 v[s[j].f].push_back(make_pair(s[j].t,s[j].q)); v[s[j].t].push_back(make_pair(s[j].f,s[j]. q)); } } dfs(1);///深搜回溯計算ans的值 double y=1.0*n*(n-1)/2; printf("%lld %.2lf\n",sum,(double)ans/y); } return 0; }