HDU 5961 传递(CCPC2016合肥) 建反图判断传递性

http://acm.hdu.edu.cn/showproblem.php?pid=5961
Problem Description
我们称一个有向图G是传递的,当且仅当对任意三个不同的顶点a,若G中有 一条边从a到b且有一条边从b到c ,则G中同样有一条边从a到c。
我们称图G是一个竞赛图,当且仅当它是一个有向图且它的基图是完全图。换句 话说,将完全图每条边定向将得到一个竞赛图。
下图展示的是一个有4个顶点的竞赛图。
在这里插入图片描述

现在,给你两个有向图P = (V,Ep)和Q = (V,Ee),满足:

  1. EP与Ee没有公共边;
  2. (V,Ep⋃Ee)是一个竞赛图。
    你的任务是:判定是否P,Q同时为传递的。

Input
包含至多20组测试数据。
第一行有一个正整数,表示数据的组数。
对于每组数据,第一行有一个正整数n。接下来n行,每行为连续的n个字符,每 个字符只可能是’-’,’P’,’Q’中的一种。
∙如果第i行的第j个字符为’P’,表示有向图P中有一条边从i到j;
∙如果第i行的第j个字符为’Q’,表示有向图Q中有一条边从i到j;
∙否则表示两个图中均没有边从i到j。
保证1 <= n <= 2016,一个测试点中的多组数据中的n的和不超过16000。保证输入的图一定满足给出的限制条件。

Output
对每个数据,你需要输出一行。如果P! Q都是传递的,那么请输出’T’。否则, 请输出’N’ (均不包括引号)。

Sample Input
4
4
-PPP
–PQ
—Q

4
-P-P
–PQ
P–Q

4
-PPP
–QQ

–Q-
4
-PPP
–PQ

–Q-

Sample Output
T
N
T
N

思路: f l o y d floyd floyd可以在 O ( n 3 ) O(n^3) O(n3)内求出传递闭包,但是时间复杂度太高了。传递性的定义:对于任意三个顶点 u , v , w u,v,w u,v,w,如果 u − > v u->v u>v v − > w v->w v>w那么一定有 u − > w u->w u>w,根据这个定义不难发现 d f s dfs dfs就可以判断一个图是否具有传递性,在 d f s dfs dfs中多记录一个上次访问的结点,设为 p r e pre pre,当前正在访问的结点,设为 c u r cur cur,那么已知: p r e − > c u r pre->cur pre>cur,只要对于任意的 u − > p r e u->pre u>pre均有 u − > c u r u->cur u>cur成立,不就能保证传递性了么,怎么得到 u u u呢,建一个反图就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

const int maxn=3005;

vector<int> vec[maxn];
vector<int> rvec[maxn];
bool vis[maxn];
char s[maxn][maxn];
int n,t;

void init()
{
	for(int i=0;i<n;i++)
			vec[i].clear(),rvec[i].clear();
		memset(vis,0,sizeof(vis));
}

bool dfs(int pre,int cur,char ch)
{
	int len=rvec[pre].size();
	int tmp;
	for(int i=0;i<len;i++)//判断传递性
	{
		tmp=rvec[pre][i];
	//	cout<<tmp<<' '<<pre<<' '<<cur<<' '<<s[tmp][cur]<<' '<<ch<<endl;
		if(s[tmp][cur]!=ch)
			return 0;
	}
	vis[cur]=1;
	len=vec[cur].size();
	for(int i=0;i<len;i++) //dfs判断每一个结点
		if(!vis[vec[cur][i]]&&!dfs(cur,vec[cur][i],ch))
			return 0;
	return 1;
}

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%s",s[i]);
		init();
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				if(s[i][j]=='P')
					vec[i].push_back(j),rvec[j].push_back(i);//原图和反图
		bool flag=1;
		for(int i=0;i<n;i++)
			if(vec[i].size()!=0&&!dfs(i,i,'P'))
				flag=0;
		if(!flag)
		{
			printf("N\n");
			continue;
		}
		init();
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				if(s[i][j]=='Q')
					vec[i].push_back(j),rvec[j].push_back(i);
		for(int i=0;i<n;i++)
			if(vec[i].size()!=0&&!dfs(i,i,'Q'))
				flag=0;
		if(flag)
			printf("T\n");
		else
			printf("N\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值