hdu1811Rank of Tetris--結題報告

題意很明瞭,但是我不知道是不是有人和我一樣,一開始在糾結一個愚蠢的問題:

0 > 1, 0 > 2; 應該是能確定的,因爲0 大於1,0大於2,那麼1和2又可以按人品排名,應該是OK呀。。。。好吧,實在是智商捉急..答案是不確定的,因爲按人品排名,我們只能是在 1 = 2 的情況下,沒有說明 = ,那麼怎麼可以認爲等級一樣呢?

下面開始詳細題解分析:

最開始做的時候,是在圖論500題上看到的,分類是 拓撲+並查集,兩個基本知識點都不難,我就上了。。誰知道。。。首先要想到,然後就是細心,我的處理方式是這樣的:

1,先把等級一樣的 ,也就是有‘=’ 的兩個點並在一個並查集裏面,也就是說,等級相同的,我們就看做一個點,用祖先root表示,其他的先不處理,用數組 a[i]  ch[i] b[i] 保存起來

2,然後我們再for(0 ~ M) 條邊,遍歷一次,用數組鄰接表建有向圖,記錄入度,同時記錄新圖當中的點數node_num(這裏等級相同的在一個集合裏面看做一個點root)

3,然後就開始 拓撲排序,那麼這裏需要記錄的情況比較雜亂,要細心

4,首先將入度爲 0 的點入隊,(這裏用隊列+鄰接表來拓撲也是第一次,每次出隊一個 u,我們對其進行拓撲,記錄拓撲的點數 topo_num ++;然後用鄰接表遍歷鄰接點  v--也就相當於刪邊操作,遍歷過程中,鄰接點 v 入度--,如果減完之後爲0,那麼入隊,同時,記錄一個節點刪除會帶來的入度爲0的點數 num ++,如果num > 1,那麼這裏排名就不確定啦),那麼上面說的就是用隊列+鄰接表處理拓撲排序的一般方法,我們在這個題目中需要添加一點點東西,在對於出隊的點,用鄰接表遍歷鄰接點的時候,我們就把u 和 v 並在一個並查集裏面,這裏是爲了後面判斷拓撲完後,所有的點是不是在一個集合裏面

OK 基本的方法就在上面啦,那麼這裏主要情況比較複雜就在於什麼時候我們是衝突,什麼時候不確定,什麼時候是OK,這裏我這樣判斷的:


OK 上馬:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>

using namespace std;

#define MAX 10005

int N,M;

int degree[MAX];//記錄入度
int father[MAX];//並查集

struct Edge
{
    int to,next;
} edge[MAX*2];
int head[MAX],tol;

void add(int u,int v)
{
    edge[tol].to = v;
    edge[tol].next = head[u];
    head[u] = tol++;
}

int find(int x)
{
    if(x != father[x]){
        father[x] = find(father[x]);
    }
    return father[x];
}

char ch[MAX];
int a[MAX],b[MAX];
void initMap()
{
    for(int i = 0; i < N; i ++) father[i] = i;
    for(int i = 0; i < M; i ++)
    {
        scanf("%d %c %d",&a[i],&ch[i],&b[i]);
        if(ch[i] == '=') //同一級別的放在一個集合中
        {
            int x = find(a[i]);
            int y = find(b[i]);
            father[x] = find(y);
        }
    }
    //第二步來建圖
    tol = 0;
    memset(head,-1,sizeof(head));
    memset(degree,0,sizeof(degree));
    for(int i = 0; i < M; i ++)
    {
        int x = find(a[i]),y = find(b[i]);
        if(ch[i] == '>'){
            add(x,y); degree[y] ++;
        }
        else if(ch[i] == '<'){
            add(y,x); degree[x] ++;
        }
    }
}

int topo()
{
    int node_num = 0;//新建圖的點數
    queue<int>q;
    for(int i = 0; i < N; i ++)
    {
        if(father[i] == i){
            node_num ++;
            if(degree[i] == 0) q.push(i);
        }
    }

    int topo_num = 0;//經過topo的點數
    bool unsure = false;//是不是出現不確定情況
    while(!q.empty())
    {
        int num = 0;//這一輪過程中產生入度爲0的點數
        int u = q.front();
        q.pop();
        topo_num++;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            degree[v] --;
            father[find(u)] = find(v); //併入

            if(!degree[v]){
                num++; q.push(v);
            }
        }
        if(num > 1) unsure = true;//就已經說明不確定啦
    }

    bool inAset = true;
    for(int i = 0; i < N-1; i ++){
        if(find(i) != find(i+1)){ //兩兩之間在一個集合
            inAset = false;
        }
    }//下面的判斷結合上圖的分析
    if(topo_num != node_num) return 1;
    else{
        if(inAset){
            if(unsure) return 2;
            else return 0;
        }else return 2;
    }
}

int main()
{
    while(cin >> N >> M)
    {
        initMap();
        int ans = topo();
        if(ans == 1) cout<<"CONFLICT"<<endl;
        else if(ans == 2) cout << "UNCERTAIN" <<endl;
        else cout << "OK" <<endl;
    }
    return 0;
}
OK 個人愚昧觀點,歡迎指正與討論
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章