洛谷 P1407 稳定婚姻

题目大意

 我们已知n对夫妻的婚姻状况,称第i对夫妻的男方为Bi,女方为Gi。若某男Bi与某女Gj曾经交往过(i≠j),则当某方与其配偶(即Bi与Gi或Bj与Gj)感情出现问题时,他们有私奔的可能性。设Bi和其配偶Gi感情不和,于是Bi和Gj旧情复燃,进而Bj因被戴绿帽而感到不爽,联系上了他的初恋情人Gk……一串串的离婚事件接踵而至。若在Bi和Gi离婚的前提下,这2n个人最终依然能够结合成n对情侣,那么我们称婚姻i为不安全的,否则婚姻i就是安全的。

给定所需信息,你的任务是判断每对婚姻是否安全。

输入 

第一行为一个正整数n,表示夫妻的对数;

以下n行,每行包含两个字符串,表示这n对夫妻的姓名(先女后男),由一个空格隔开;

第n+2行包含一个正整数m,表示曾经相互喜欢过的情侣对数;

以下m行,每行包含两个字符串,表示这m对相互喜欢过的情侣姓名(先女后男),由一个空格隔开。

输出

输出文件共包含n行,第i行为“Safe”(如果婚姻i是安全的)或“Unsafe”(如果婚姻i是不安全的)。


其实就是一道求强连通分量的题 建图是夫妻中girl->boy 情侣是boy->girl

若一对夫妻处在一个强连通分量里面 则婚姻就不安全 反之则安全 

然后跑tarjan很快乐

刚开始老是有几个点re 把数组开的māng子一样的大之后就变成了t...

后来才发现是因为tarjan里面访问点条件写错了 是!dfn[v] 不是!vis[v]

因为如果一个强连通分量找完后出栈了就变成了false 如果又有点指向它就会再次进行访问 就可能爆掉

还有就是学到了一个很有用的东西 map 函数 其实就是不同数据类型下标的互相映射 爆好用...!!

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>

using namespace std;

const int N=100000+10;

struct person{
	char name[10];
	int num;
}p[N];

int idc,n,cnt,stk[N],top,tot,color[N];
int mapp,nex[2*N],tov[2*N],head[N],m,dfn[N],low[N];
char name1[10],name2[10];
bool vis[N];

void add(int u,int v){
	tot++;
	nex[tot]=head[u];
	tov[tot]=v;
	head[u]=tot;
}

int min(int a,int b){
	return a<b?a:b;
}

void tarjan(int x){
	dfn[x]=low[x]=++idc;
	stk[++top]=x;
	vis[x]=true;
	for(int i=head[x];i;i=nex[i]){
		int v=tov[i];
		if(vis[v]){
			low[x]=min(low[x],dfn[v]);
		}
		if(!dfn[v]){//惨痛教训 
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
	}
	if(low[x]==dfn[x]){
		mapp++;
		while(1){
			int u=stk[top];
			color[u]=mapp;
			vis[u]=false;
			top--;
			if(u==x) break;
		}
	}
}

map<string,int> pl;

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%s%s",name1,name2);
		pl[name1]=++cnt;
		pl[name2]=++cnt;
		add(cnt-1,cnt);
	}
	
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		scanf("%s%s",name1,name2);
		add(pl[name2],pl[name1]);
	}
	
	for(int i=1;i<=cnt;i++)
		if(!dfn[i]) tarjan(i);
	
	for(int i=1;i<=n;i++){
		int g=2*i-1;
		int b=g+1;
		if(color[b]==color[g]) printf("Unsafe\n");
		else printf("Safe\n");
	}
	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值