思路:
- 带权并查集+枚举。
- 关键在题意:
- 若按照一般做法,当出现矛盾时完全无法判断是关系链上的哪一环导致出错,也就无法确定 judge 的身份。
- 枚举每一个人,认为此人是 judge 时,有关他的所有关系都是不可信的,需要跳过。
- 题目不仅要求出谁是 judge,还要求判断多少条关系后能够确认他是 judge,等价于能够判断所有其他人都不是 judge 的语句数量。
- 所以,EDGE 存边、枚举 judge 得到 ans[0…N)、cnt 存 ans[i]==0的 i 的数量(cnt=0:impossible 不符合题意;cnt=1:找到 judge;cnt>1:条件不全导致无法确定 judge)。
代码:
//329ms 720kB
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 505;
int N,M;
int ans[maxn];int cnt;
int par[maxn];
int val[maxn];
struct EDGE{
int l,r,w;
}edge[maxn * 4];
void ADDEDGE(int m,int w,int l,int r){
edge[m].l = l;
edge[m].r = r;
edge[m].w = w;
return ;
}
void INIT(){
memset(par , -1 , sizeof(par));
memset(val , 0 , sizeof(val)) ;
return ;
}
int FIND(int i){
if(par[i] == -1)
return i;
int tp = par[i];
par[i] = FIND(par[i]);
val[i] = (val[i] + val[tp]) % 3;
return par[i];
}
void UNION(int w,int l,int r,int parl,int parr){
par[parr] = parl;
val[parr] = (val[l] + w - val[r] + 3) % 3;
return ;
}
int main(){
while(cin>>N>>M){
cnt = 0;
memset(ans , 0 , sizeof(ans));
for(int m=1;m<=M;m++){
int l,r,w;
char op;
scanf("%d%c%d" , &l , &op , &r);
if(op == '>')
w = 1;
else if(op == '=')
w = 0;
else
w = 2;
ADDEDGE(m , w , l , r);
}
for(int i=0;i<N;i++){
//judge = i
INIT();
for(int m=1;m<=M;m++){
int l = edge[m].l;
int r = edge[m].r;
int w = edge[m].w;
if(l == i || r == i)
continue;
int parl = FIND(l);
int parr = FIND(r);
if(parl == parr){
if((val[l] + w) % 3 != val[r]){
ans[i] = m;
break;
}
}
else
UNION(w , l , r , parl , parr);
}
}
int id;
for(int i=0;i<N;i++)
if(!ans[i]){
cnt++;
id = i;
}
if(!cnt){
//去了哪个,剩下的都会出现矛盾,impossible
printf("Impossible\n");
}
else if(cnt == 1){
//got it
int tp = 0;
for(int i=0;i<N;i++)
tp = max(tp , ans[i]);
printf("Player %d can be determined to be the judge after %d lines\n" , id , tp);
}
else{
//去了一些,剩下的都没有矛盾,ambiguous
printf("Can not determine\n");
}
}
return 0;
}