【CODEVS】2833 奇怪的夢境

2833 奇怪的夢境

時間限制: 1 s
空間限制: 128000 KB
題目等級 : 黃金 Gold
題目描述 Description

Aiden陷入了一個奇怪的夢境:他被困在一個小房子中,牆上有很多按鈕,還有一個屏幕,上面顯示了一些信息。屏幕上說,要將所有按鈕都按下才能出去,而又給出了一些信息,說明了某個按鈕只能在另一個按鈕按下之後才能按下,而沒有被提及的按鈕則可以在任何時候按下。可是Aiden發現屏幕上所給信息似乎有矛盾,請你來幫忙判斷。

輸入描述 Input Description

第一行,兩個數N,M,表示有編號爲1...N這N個按鈕,屏幕上有M條信息。

接下來的M行,每行兩個數ai,bi,表示bi按鈕要在ai之後按下。所給信息可能有重複,保證ai≠bi。

輸出描述 Output Description

若按鈕能全部按下,則輸出“o(∩_∩)o”。

若不能,第一行輸出“T_T”,第二行輸出因信息有矛盾而無法確認按下順序的按鈕的個數。輸出不包括引號。

樣例輸入 Sample Input

3 3

1 2

2 3

3 2

樣例輸出 Sample Output

T_T

2

數據範圍及提示 Data Size & Hint

對於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;
}


用的邊集數組寫的,強行改習慣。

發佈了40 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章