1. 程式人生 > >codevs2833 奇怪的夢境 x

codevs2833 奇怪的夢境 x

初始 ccf click 耗時 top event 出了 mem esp

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。

思路:

  拓撲排序,兩種寫法,各有千秋.

代碼一:

技術分享
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>

using namespace std;

const int M = 10005;
int n,m,ans,now;
int
ru[M],head[M]; queue<int>q; struct A{ int next,to; }e[4*M]; void add(int u,int v) { e[++now].to=v; e[now].next=head[u]; head[u]=now; } void topo() { for(int i=1;i<=n;i++) if(ru[i]==0) q.push(i); while(!q.empty()) { int u=q.front(); q.pop(); ans++; for(int i=head[u];i!=-1;i=e[i].next) { int v=e[i].to; ru[v]--; if(ru[v]==0) q.push(v); } } if(ans<n) { printf("T_T\n%d",n-ans); return; } else { printf("o(n_n)o"); return; } } int main() { scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); for(int i=1,a,b;i<=m;i++) { scanf("%d%d",&a,&b); add(a,b); ru[b]++; } topo(); return 0; }
隊列版

技術分享

缺點:

  內存占用較多.

優點:

  耗時少.

代碼二:

技術分享
#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;

const int M = 10005;
int n,m,k,ans;
bool dad[M][M];
int ru[M];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,a,b;i<=m;i++)
    {
        ///b在a之前按下 
        scanf("%d%d",&a,&b);
        if(!dad[a][b]) ru[b]++; 
        dad[a][b]=true;
    }
    for(int i=1;i<=n;i++)
    {
        k=1;///記得將k進行初始化,防止漏下找入度為零的點 
        while(k<=n && ru[k]!=0) k++;///找入度為零的點 
        if(k<=n && ru[k]==0)
        {
            ans++;///統計答案個數 
            ru[k]=-1;///刪邊 
            for(int j=1;j<=n;j++)
                if(dad[k][j])
                    ru[j]--;///刪邊 
        }
    }
    if(ans==n) printf("o(n_n)o");
    else printf("T_T\n%d\n",n-ans);
    return 0;
}
for循環版

技術分享

缺點:

  耗時多.(是第一種的差不多50倍)

優點:

  內存占用較少.

Ps:如果讓我進行選擇,我會毫不猶豫的選擇第一種,因為跑得快!!!

codevs2833 奇怪的夢境 x