1. 程式人生 > >程式自動分析 (hash + 並查集)

程式自動分析 (hash + 並查集)

在實現程式自動分析的過程中,常常需要判定一些約束條件是否能被同時滿足。

考慮一個約束滿足問題的簡化版本:假設$$x_1$$,$x_2$,$x_3$,…代表程式中出現的變數,給定n個形如$x_i=x_j$或$x_i≠x_j$的變數相等/不等的約束條件,請判定是否可以分別為每一個變數賦予恰當的值,使得上述所有約束條件同時被滿足。例如,一個問題中的約束條件為:$x_1=x_2$,$x_2=x_3$,$x_3=x_4$,$x_1≠x_4$,這些約束條件顯然是不可能同時被滿足的,因此這個問題應判定為不可被滿足。

現在給出一些約束滿足問題,請分別對它們進行判定。

Input

輸入檔案的第1行包含1個正整數t,表示需要判定的問題個數。注意這些問題之間是相互獨立的。

對於每個問題,包含若干行:

第$1$行包含$1$個正整數$n$,表示該問題中需要被滿足的約束條件個數。

接下來$$n$$行,每行包括3個整數$i,j,e$,描述$1$個相等/不等的約束條件,相鄰整數之間用單個空格隔開。若$e=1$,則該約束條件為$x_i=x_j$;若$e=0$,則該約束條件為$xi≠xj$。

Output

輸出檔案包括t行。

輸出檔案的第k行輸出一個字串“YES”或者“NO”(不包含引號,字母全部大寫),“YES”表示輸入中的第k個問題判定為可以被滿足,“NO”表示不可被滿足。

Sample Input

2 2 1 2 1 1 2 0 2 1 2 1 2 1 1

Sample Output

NO YES

$1≤n≤1000000$

$1≤i,j≤1000000000$

分析

題目中給了相等關係和不等關係,把題目中的元素劃為兩類。判斷是不是可以同時滿足其實就是在判斷可不可以不交叉的劃分成兩個部分。因此可以用並查集來解決。同時資料範圍又很大,所以想到了對映到map 中用map來解決。

程式碼

int findfa(int x)
{
    int y = x;
    while(fa[x]!=x) x=fa[x];
    while(fa[y]!=x)
    {
        int t = fa[y];
        fa[y] = x;
        y = t;
    }
    return x;
}
int main ()
{
    int T;
    sd(T);
    while(T--)
    {
        int m;
        sd(m);
        int cnt = 0;
        map<int,int>mp;
        r0(i,m)
        {
            sddd(a[i],b[i],e[i]);
            if(mp.count(a[i])==0) 
                mp[a[i]]=++cnt;
            if(mp.count(b[i])==0)
                mp[b[i]]=++cnt;
            a[i]=mp[a[i]];
            b[i]=mp[b[i]]; 
        }
        r1(i,cnt)   fa[i] = i;
        r0(i,m)
        {
            if(e[i] == 0) continue;
            int fx = findfa(a[i]);
            int fy = findfa(b[i]);
            if(fx!=fy) fa[fx] =fy;
        }
        bool flag = true;
        r0(i,m)
        {
            if(e[i]) continue;
            int fx = findfa(a[i]);
            int fy = findfa(b[i]);
            if(fx == fy)
            {
                flag = false;
                break;
            }
        }
        if(flag) puts("YES");
        else puts("NO");
    }
    return 0;
}