题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1811
题意:
给定一个你n个点有向图,告诉你m个 ( a , b , c ) (a,b,c) (a,b,c)这种关系,a,c表示节点编号,b属于’>’,’<’,’=’,三种中的一中,’>‘代表a名次高于c,小于则c名次高于a,’=‘说明名次相同,但这些相互有’='关系的元素集合中,编号小的名次高,问是否能确定一个从高到低名次关系,能则输出"OK",不存在一个从高到低排名则输出"CONFLICT",若存在排名,但是不唯一,则输出"UNCERTAIN";
分析:
拓扑排序+并查集,大致就是将具有’='关系的元素,缩成一个点,然后进行建图,不能边缩点,边建图,(不然会wa到怀疑人生);建完图,此时记录你建的图的总的点的个数cnt,然后再拓扑排序,在拓扑排序过程中记录遇到点的个数num,如果num不等于cnt显然存在环;对于只存在一条拓扑排序路径的问题,假设过程中你将一个入度为0的a点删去,然后删去它所连接的边(a,b)(a,c)…,然后此时你发现a所连接的点有超过两个及两个以上入度等于0,假设就是b和c吧,bc之间显然没有边相连,因为入度均为0,那么b,c谁排名高呢,这便会产生两种拓扑排序结果,所以在拓扑排序的过程中要仅有一个结果,那么在拓扑排序的任意时刻,你的队列里(队列里的元素入度为0)最多只能有一个点,不能超过1个;若这两种情况均不存在,那么就是"OK"了;
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define inf 0x7f7f7f7f
#define maxn 100411
#define N 20500
#define mod 100003
typedef long long ll;
typedef struct{
int u,v,next,w;
}Edge;
Edge e[N];
int cnt,head[N];
inline void add(int u,int v){
e[cnt].u=u;
e[cnt].v=v;
//e[cnt].w=w;
// e[cnt].f=f;
e[cnt].next=head[u];
}
inline void write(int x)
{
if(x<0)
putchar('-'),x=-x;
if(x>9)
write(x/10);
putchar(x%10+'0');
}
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int n,m,f[N],in[N],a[N],b[N];
char ch[N];
int find(int x){
return x==f[x]?x:f[x]=find(f[x]);
}
void init(){
for(int i=0;i<n;i++) {
f[i] = i;
}
}
void topsort(){
queue<int>q;
int flag=0,num1=0,num2=0;
for(int i=0;i<n;i++) {
if (find(i) == i) {
if (!in[i])
q.push(i);
num2++;
}
}
while(!q.empty()) {
int u = q.front();q.pop();
num1++;
if (!q.empty())flag = 1;
for (int i = head[u]; i!=-1;i = e[i].next){
int v = e[i].v;
in[v]--;
if (!in[v])q.push(v);
}
}
if(num1!=num2)cout<<"CONFLICT"<<endl;
else if(flag==1)cout<<"UNCERTAIN"<<endl;
else cout<<"OK"<<endl;
}
int main() {
while(scanf("%d%d",&n,&m)!=EOF){
cnt=0;
init();
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(ch,0,sizeof(ch));
memset(head,-1,sizeof(head));
memset(in,0,sizeof(in));
for(int i=0;i<m;i++){
scanf("%d %c %d",&a[i],&ch[i],&b[i]);
int fx=find(a[i]),fy=find(b[i]);
if(ch[i]=='='){//一定先合并完集合再进行建图
f[fx]=fy;
}
}
for(int i=0;i<m;i++){
int fx=find(a[i]),fy=find(b[i]);
if(ch[i]=='+')continue;
else if(ch[i]=='>'){
add(fx,fy);
in[fy]++;
}
else if(ch[i]=='<'){
add(fy,fx);
in[fx]++;
}
}
topsort();
}
return 0;
}
我们坚持一件事情,并不是因为这样做了会有效果,而是坚信,这样做是对的。
——哈维尔
本文探讨了如何使用拓扑排序和并查集解决特定的有向图问题,详细介绍了算法流程,包括元素合并、建图、拓扑排序判断等关键步骤。

2472

被折叠的 条评论
为什么被折叠?



