hdu 1811
題目大意:
有n個點,m個序關系,關系只有三種表示方式:a < b , a > b , a = b,問根據給定的關系能否將n個點排成有序,能輸出OK,若兩點之間出現 a < b 且 b < a 的情況,則輸出CONFLICT,若出現某些點間的關系不確定,則輸出UNCERTAIN,若后兩者同時存在,則優先輸出CONFLICT。
題目分析:
由于存在 a = b 的情況,所以需要用并查集,將所有相等的點進行縮點,然后進行拓撲排序,注意!優先輸出CONFLICT。
#include<stdio.h>
#include<string.h>
#include<vector>
#define N 10010
using namespace std;
vector<int>g[N];
int rank[N], f[N], m,n,num[N], save[20005][2], q[N];
int find(int x)//并查集的查找函數
{
if(f[x]==x) return x;
else return f[x]=find(f[x]); //路徑壓縮,而且這樣寫對匯編源碼有優化,不容易因遞歸層數太多出現棧溢出,但僅是不易!
}
void Union(int x, int y)//并查集的合并操作
{
int a=find(x);
int b=find(y);
if(a!=b){
if(rank[a]<rank[b]) //通過判斷兩個集合的大小,將小集合并入大集合,減少遞歸層數,算是個優化
f[a]=b, rank[b]+=rank[a];
else
f[b]=a, rank[a]+=rank[b];
}
}
int main()
{
int i,j,k,a,b,ans,tmp,x,y,tim;
char s[5];
while(scanf("%d %d",&n,&m)!=EOF){
for(i=0; i<=n; i++){
f[i]=i;
g[i].clear();
rank[i]=1;
}
memset(num, 0, sizeof(num));
k=0;
for(i=0; i<m; i++){
scanf("%d %s %d",&a, s, &b);
if(s[0]!='='){
if(s[0]=='>'){
tmp=a;
a=b;
b=tmp;
}
save[k][0]=a, save[k++][1]=b;
}
else if(s[0]=='='){
Union(a, b);
}
}
for(i=0; i<k; i++){
x=find(save[i][0]);
y=find(save[i][1]);
g[x].push_back(y);
num[y]++;
}
ans=0;
int head=0, tail=0; //從這里開始拓撲排序,用隊列做保險啊,直接寫的話很容易錯
k=0;
for(i=0; i<n; i++)
if(num[i]==0 && find(i)==i){ //找到第一輪入度(num[])為零的點入隊。
q[tail++]=i;
k++;
}
if(k==0)
ans=2;
else{
if(k>1) ans=1; //本題要求
while(head<tail){ //排序過程
x=q[head++]; num[x]=-1; k=0;
for(i=0; i<g[x].size(); i++){
y=g[x][i];
num[y]--;
if(num[y]==0)
q[tail++]=y, k++;
}
if(k>1) ans=1; //本題要求
}
for(i=0; i<n; i++) //本題要求
if(num[i]>0){
ans=2;
break;
}
}
if(ans==0)
printf("OK\n");
else if(ans==1)
printf("UNCERTAIN\n");
else if(ans==2)
printf("CONFLICT\n");
}
return 0;
}