hdoj 4857 逃生(逆向拓撲排序+優先佇列)
阿新 • • 發佈:2019-02-05
逃生
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2161 Accepted Submission(s): 608
Problem Description 糟糕的事情發生啦,現在大家都忙著逃命。但是逃命的通道很窄,大家只能排成一行。
現在有n個人,從1標號到n。同時有一些奇怪的約束條件,每個都形如:a必須在b之前。
同時,社會是不平等的,這些人有的窮有的富。1號最富,2號第二富,以此類推。有錢人就賄賂負責人,所以他們有一些好處。
負責人現在可以安排大家排隊的順序,由於收了好處,所以他要讓1號儘量靠前,如果此時還有多種情況,就再讓2號儘量靠前,如果還有多種情況,就讓3號儘量靠前,以此類推。
那麼你就要安排大家的順序。我們保證一定有解。
Input 第一行一個整數T(1 <= T <= 5),表示測試資料的個數。
然後對於每個測試資料,第一行有兩個整數n(1 <= n <= 30000)和m(1 <= m <= 100000),分別表示人數和約束的個數。
然後m行,每行兩個整數a和b,表示有一個約束a號必須在b號之前。a和b必然不同。
Output 對每個測試資料,輸出一行排隊的順序,用空格隔開。
Sample Input 1 5 10 3 5 1 4 2 5 1 2 3 4 1 4 2 3 1 5 3 5 1 2
Sample Output 1 2 3 4 5
題目是求拓撲排序,但不是按照字典序最小輸出,而是要使較小的數排在最前面。
一開始的錯誤思路:給每個點確定一個優先順序(該點所能到達的最小的點),然後用拓撲排序+優先對列正向處理,正向輸出。這是錯誤的,如下樣例:
1
5 4
5 2
4 3
2 1
3 1
正確的解法:是反向建邊,點大的優先順序高,用拓撲排序+優先佇列,逆向輸出序列即可。
根據每對限制,可確定拓撲序列,但此時的拓撲序列可能有多個(沒有之間關係的點的順序不定)。本題要求較小的點排到前面,則可確定序列。
(1)如果點a和點b有直接和簡接的拓撲關係,那麼a和b的先後順序可有拓撲排序確定。
(2)如果點a和點b沒有直接和簡接的拓撲關係,那麼a和b的先後順序由a和b所能到達的點的確定。
如:
1
3 2
3 1
3 1
應輸出結果為 3 1 2
點3 和 點2 沒有直接的拓撲關係,但是3到達最小點為1,2到達最小點為2。
綜合(1)和(2)本題需要逆向處理。
PS:歐拉回路的路徑輸出也是逆向輸出的。
注意 vector STL中在此處的用處 方便
</pre><pre name="code" class="cpp">#include<cstdio> #include<queue> #include<vector> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; #define MAX 100005 priority_queue<int,vector<int>,less<int > >q;//此處應該為從大到小 vector<int> map[MAX]; int indegree[MAX]; int que[MAX];//儲存 void topsort(int n) { int i; for(i=1;i<=n;i++) if(indegree[i]==0) q.push(i); int j=0; memset(que,0,sizeof(que)); while(!q.empty()) { int v=q.top(); q.pop(); que[j++]=v; for(int k=0;k<map[v].size();k++) { indegree[map[v][k]]--; if(indegree[map[v][k]]==0) q.push(map[v][k]); } } for(i=j-1;i>0;i--) printf("%d ",que[i]); printf("%d\n",que[0]); } int main() { int T; scanf("%d",&T); while(T--) { int n,m; scanf("%d%d",&n,&m); memset(indegree,0,sizeof(indegree)); for(int i=1;i<=n;i++) map[i].clear();</span> while(m--) { int a,b; scanf("%d%d",&a,&b); map[b].push_back(a);//將a放在b的後邊,建立反向邊的關係</span> indegree[a]++; } topsort(n); } return 0; }
剛開始我以為這樣做WA
#include<cstdio>
#include<cstring>
#include<queue>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define MAX 100005
int head[MAX];
int indegree[MAX];
int que[MAX];
struct node
{
int to;
int next;
}edge[MAX];
priority_queue<int,vector<int>,greater<int> >q;
//queue<int>q;
void topsort(int n)
{
int i;
for(i=1;i<=n;i++)
if(indegree[i]==0)
q.push(i);
int j=0;
memset(que,0,sizeof(que));
while(!q.empty())
{
int v=q.top();
//int v=q.front();
q.pop();
que[j++]=v;
for(int k=head[v];k!=-1;k=edge[k].next)
{
indegree[edge[k].to]--;
if(indegree[edge[k].to]==0)
q.push(edge[k].to);
}
}
for(i=j-1;i>0;i--)
printf("%d ",que[i]);
printf("%d\n",que[0]);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m,i;
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
memset(indegree,0,sizeof(indegree));
memset(edge,0,sizeof(edge));
for(i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
// edge[i].to=a;
// edge[i].next=head[b];
// head[b]=i;
// indegree[a]++;
edge[i].to=b;
edge[i].next=head[a];
head[a]=i;
indegree[b]++;
}
topsort(n);
}
}