Liars and Truth Tellers 真假奶牛
- Description
约翰有N头奶牛,有一部分奶牛是真话奶牛,它们只说真话,而剩下的是假话奶牛,只说假话。有一天,约翰从奶牛的闲谈中陆续得到了M句话,第i句话出自第Xi头奶牛,它会告诉约翰第Yi头是一头真话奶牛还是假话奶牛。然而,约翰记性不好,他可能把这些话的内容记错了。请检查一下 约翰的记录是否会有矛盾,帮助他找到一个尽量大的K使得约翰记下的前K句话不矛盾。
- Input Format
第一行:两个整数 N 和 M ,1 ≤ N ≤ 1000; 1 ≤ M ≤ 10000
• 第二行到 M + 1 行:第 i + 1 行有两个整数:Xi 和 Yi,1 ≤ Xi, Yi ≤ N ,接下来有一个字符:
– 如果是 T ,表示 Xi 说 Yi 是真话奶牛;
– 如果是 L,表示 Xi 说 Yi 是假话奶牛;
- Output Format
单个整数,即表示题目描述中的K
- Sample Input
4 3
1 4 L
2 3 T
4 1 T
- Sample Output
2
- Hint
解释
前两句没有矛盾, 但第一句和第三句存在矛盾
- 分析
若x说y是假,则x真y假或x假y真;
若x说y是真,则x真y真或x假y假。
然后我们二分一个答案Lim,对于边的编号在2*Lim以内的做二分图染色。对于状况一染不同的颜色,状况二染相同的颜色。如果能够顺利跑完,说明到第Lim句话之前都是没有矛盾的。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1001,M=10001;
int n,m,u,v,Ans,tot,last[N],Colored[N];
char ch;
struct Edge{int to,next,c;}E[2*M];
void Addline(int u,int v,int w){
E[++tot].to=v; E[tot].next=last[u]; E[tot].c=w; last[u]=tot;
E[++tot].to=u; E[tot].next=last[v]; E[tot].c=w; last[v]=tot;
}
bool Color(int u,int Lim){
for (int i=last[u];i;i=E[i].next){
if (i>Lim) continue;
int v=E[i].to;
if (E[i].c==0){
if (Colored[v]==-1){
Colored[v]=1-Colored[u];
if (!Color(v,Lim)) return false;
}
else if (Colored[v]!=1-Colored[u]) return false;
}
else{
if (Colored[v]==-1){
Colored[v]=Colored[u];
if (!Color(v,Lim)) return false;
}
else if (Colored[v]!=Colored[u]) return false;
}
}
return true;
}
bool Check(int Lim){
memset(Colored,-1,sizeof(Colored));
for (int i=1;i<=n;i++)
if (Colored[i]==-1){
Colored[i]=0;
if (!Color(i,Lim)) return false;
}
return true;
}
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d%d\n",&n,&m);
for (int i=1;i<=m;i++){
scanf("%d%d %c\n",&u,&v,&ch);
Addline(u,v,ch=='T');
}
for (int L=0,R=m,Mid=(L+R)>>1;L<=R;Mid=(L+R)>>1)
if (Check(2*Mid)) Ans=Mid,L=Mid+1;
else R=Mid-1;
printf("%d",Ans);
fclose(stdin); fclose(stdout);
return 0;
}