2833 奇怪的夢境
Aiden陷入了一個奇怪的夢境:他被困在一個小房子中,牆上有很多按鈕,還有一個屏幕,上面顯示了一些信息。屏幕上說,要將所有按鈕都按下才能出去,而又給出了一些信息,說明了某個按鈕只能在另一個按鈕按下之後才能按下,而沒有被提及的按鈕則可以在任何時候按下。可是Aiden發現屏幕上所給信息似乎有矛盾,請你來幫忙判斷。
第一行,兩個數N,M,表示有編號爲1...N這N個按鈕,屏幕上有M條信息。
接下來的M行,每行兩個數ai,bi,表示bi按鈕要在ai之後按下。所給信息可能有重複,保證ai≠bi。
若按鈕能全部按下,則輸出“o(∩_∩)o”。
若不能,第一行輸出“T_T”,第二行輸出因信息有矛盾而無法確認按下順序的按鈕的個數。輸出不包括引號。
3 3
1 2
2 3
3 2
T_T
2
對於30%的數據,保證0<N≤100。
對於50%的數據,保證0<N≤2000。
對於70%的數據,保證0<N≤5000。
對於100%的數據,保證0<N≤10000,0<M≤2.5N。
給大家理一下我的思路。
首先,很顯然是一個圖。
然後,很顯然是拓撲。
最後,很顯然代碼應該是這樣。
改錯,很顯然數組開小了。
嗯。。。我在考慮要不要把這個題放到水庫裏。
還是說一下吧,拓撲排序不懂得童鞋請自行百度。
大體思路是這樣的,按鈕有先後順序,一個按下後另一個才能按下,可以理解爲一個按鈕指向另一個按鈕,按下一個按鈕再按順序按下下一個按鈕的操作當做走過一條有向邊。很顯然這是一個有向圖。
這樣不能全部按下的情況就只有一種:他們形成了環,要按A必須按下B,要按下B必須按下C,要按下C必須按下A,很顯然是矛盾的。這樣只需要找環就可以了。
有向圖找環,比較方便的是使用拓撲。當然你也可以dfs遍歷,或者tarjan,不過相對比較麻煩。
請務必記住:找環就用拓撲!拓撲後,不在拓撲序列中的點構成k個環(k>=1)。具體證明很簡單,環即從一個點沿邊走仍能走回該點,且對環中每一個點都有效,則其入度定然大於0。拓撲時不斷選擇入度爲0的點刪除並更新入度,但請注意:環中沒有任何一個點的入度將可能爲0!所以不會被刪除。
好,下面放代碼。
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int MAXN=10010;
const int MAXM=MAXN*2.5;
struct Edage
{
int to,next,w;
}edage[MAXN];
int head[MAXM];
int eCnt;
int rudu[MAXM];
void build(int u,int v,int w)
{
edage[++eCnt].to=v;
edage[eCnt].w=w;
edage[eCnt].next=head[u];
head[u]=eCnt;
}
int n,m;
int cnt;
bool top()
{
queue<int> q;
for(int i=1;i<=n;i++)
{
if(!rudu[i])
{
q.push(i);
cnt++;
}
}
while(!q.empty())
{
for(int pos=head[q.front()];pos;pos=edage[pos].next)
{
int node=edage[pos].to;
rudu[node]--;
if(rudu[node]==0)
{
q.push(node);
cnt++;
}
}
q.pop();
}
if(cnt==n)return true;
else return false;
}
int main()
{
freopen("data.txt","r",stdin);
scanf("%d%d",&n,&m);
int ai,bi;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&ai,&bi);
build(ai,bi,0);
rudu[bi]++;
}
if(top())
{
printf("o(∩_∩)o");
}
else
{
printf("T_T\n%d",n-cnt);
}
return 0;
}
用的邊集數組寫的,強行改習慣。