拓撲排序 HDU - 5695
阿新 • • 發佈:2017-07-06
+= ret include pty pri 包括 log const bin 眾所周知,度度熊喜歡各類體育活動。
今天,它終於當上了夢寐以求的體育課老師。第一次課上,它發現一個有趣的事情。在上課之前,所有同學要排成一列, 假設最開始每個人有一個唯一的ID,從1到NN,在排好隊之後,每個同學會找出包括自己在內的前方所有同學的最小ID,作為自己評價這堂課的分數。麻煩的是,有一些同學不希望某個(些)同學排在他(她)前面,在滿足這個前提的情況下,新晉體育課老師——度度熊,希望最後的排隊結果可以使得所有同學的評價分數和最大。
今天,它終於當上了夢寐以求的體育課老師。第一次課上,它發現一個有趣的事情。在上課之前,所有同學要排成一列, 假設最開始每個人有一個唯一的ID,從1到NN,在排好隊之後,每個同學會找出包括自己在內的前方所有同學的最小ID,作為自己評價這堂課的分數。麻煩的是,有一些同學不希望某個(些)同學排在他(她)前面,在滿足這個前提的情況下,新晉體育課老師——度度熊,希望最後的排隊結果可以使得所有同學的評價分數和最大。
Input第一行一個整數TT,表示T(1≤T≤30)T(1≤T≤30) 組數據。
對於每組數據,第一行輸入兩個整數NN和M(1≤N≤100000,0≤M≤100000)M(1≤N≤100000,0≤M≤100000),分別表示總人數和某些同學的偏好。
接下來 MM行,每行兩個整數AA 和B(1≤A,B≤N)B(1≤A,B≤N),表示ID為AA的同學不希望ID為BB的同學排在他(她)之前。你可以認為題目保證至少有一種排列方法是符合所有要求的。
Output對於每組數據,輸出最大分數 。Sample Input
3 1 0 2 1 1 2 3 1 3 1
Sample Output
1 2 6
對於這個題目來說,顯然可以看出這是有限制關系的偏序排序題目,拓撲排序的思想自然而然,想到思路並不難沒重點是如何處理程序並將程序寫出來;
根據個人習慣,把理解加在代碼註釋裏面。
#include <iostream> #include <string> #include <cstdio> #include <queue> #include <vector> #include <algorithm> #include <cstring> using namespace std; const int maxn = 100010; const int inf = 0x3f3f3f3f3f; vector<int> G[maxn];//由於直接用int G[maxn][maxn] 會占用大內存,有可能會爆 int indegree[maxn];//這是對點的入度標記 int main() { int T; scanf("%d",&T); int n,m; while(T--) { scanf("%d%d",&n,&m); memset(indegree,0,sizeof(indegree));//清空處理,同下 memset(G,0,sizeof(G));//第二階段代碼會對這一點進行優化; int u,v; for(int i=0;i<m;i++) { scanf("%d%d",&u,&v); G[u].push_back(v);//和點u,存在偏序關系的點 v,壓入 indegree[v]++;//哈希圖特點,偏序關系,由u->v,點v的入度++,不需要考慮出度 }//具體可以參考另一篇博文 priority_queue<int> que;//優先隊列對壓入的點進行維護 //優先隊列默認是大的在前,也就是降序 for(int k=1;k<=n;k++) if(!indegree[k]) que.push(k);//先將沒有入度的點壓入, //沒有入度的點,也就是不存在以該點為終點的偏序關系,對整體排序沒有影響 //在哈希圖上體現就是(假設哈希圖由下往上繪制),這個點是懸掛點,極小點(離散數學) long long res=0;//long long 保險,看題目吧 int u_num=inf;//考慮程序的魯棒性,定義為無限大 while(!que.empty())//隊列的維護+模擬過程 { int num=que.top();//頭,是不是有點像bfs,這就對了 que.pop();//就按照bfs代碼方式搞下去,部分改變 u_num=min(u_num,num); res+=u_num; for(int i=0;i<G[num].size();i++) { int v=G[num][i];//這是對該點排序後,刪除所有把該點作為起點的線段 //也可以理解為,除去哈希圖上的這個點->哈希圖不允許懸掛邊的存在 indegree[v]--;//對應的終點入度-- if(!indegree[v])//入度為0,對後續排序沒影響,選擇壓入 que.push(v); } } printf("%lld\n",res);//輸出總花費 } }
運行702ms;
其實可以看出來memset遍歷清空費的時間是比較大的,可以根據vector特點修改下(借鑒了求前輩的博文)
#include <iostream> #include <string> #include <cstdio> #include <queue> #include <vector> #include <algorithm> #include <cstring> using namespace std; const int maxn = 100010; const int inf = 0x3f3f3f3f3f; vector<int> G[maxn]; int indegree[maxn]; int main() { int T; scanf("%d",&T); int n,m; while(T--) { scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) { indegree[i] = 0; G[i].clear(); } int u,v; for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); G[u].push_back(v); indegree[v]++; } priority_queue<int> que; for(int k=1;k<=n;k++) if(!indegree[k]) que.push(k); long long res=0; int u_num=inf; while(!que.empty()) { int num=que.top(); que.pop(); u_num=min(u_num,num); res+=u_num; for(int i=0;i<G[num].size();i++) { int v=G[num][i]; indegree[v]--; if(!indegree[v]) que.push(v); } } printf("%lld\n",res); } }
608ms;確實,有點效果;
不知道之前我的拓撲你們看了沒,請看如下代碼;
#include <iostream> #include <string> #include <cstdio> #include <queue> #include <vector> #include <algorithm> #include <cstring> using namespace std; const int maxn = 100010; const int inf = 0x3f3f3f3f3f; vector<int> G[maxn]; int indegree[maxn]; priority_queue<int> que; void combine(int a,int b) { G[a].push_back(b); indegree[b]++; return; } void del_gre(int num) { for(int i=0;i<G[num].size();i++) { int v=G[num][i]; indegree[v]--; if(!indegree[v]) que.push(v); } return; } int main() { int T; scanf("%d",&T); int n,m; while(T--) { while(!que.empty()) que.pop(); scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) { indegree[i] = 0; G[i].clear(); } int u,v; for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); combine(u,v); } for(int k=1;k<=n;k++) if(!indegree[k]) que.push(k); long long res=0; int u_num=inf; while(!que.empty()) { int num=que.top(); que.pop(); u_num=min(u_num,num); res+=u_num; del_gre(num); } printf("%lld\n",res); } }
根據我上個關於拓撲理解寫的
670ms;
最後來一句,求關註~’
拓撲排序 HDU - 5695