1. 程式人生 > >hdoj 4857 逃生(逆向拓撲排序+優先佇列)

hdoj 4857 逃生(逆向拓撲排序+優先佇列)

逃生

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);
	}
}