石头剪刀布的竞赛图

【问题描述】
考虑 N 个人玩一个游戏,任意两个人之间进行一场游戏(共 N*(N-1)/2 场),
且每场一定能分出胜负。
现在,你需要在其中找到三个人构成“剪刀石头步”局面:三个人 A,B,C
满足 A 战胜 B,B 战胜 C,C 战胜 A。
【输入格式】
第一行一个正整数 N,表示参加游戏的人数。
接下来 N 行,每行 N 个数 0/1,中间没有空格隔开。第 i 行第 j 列数字为 1
表示 i 在游戏中战胜了 j。所有对角线元素(即第 i 行第 i 个元素)为 0,保证数
据合法。
【输出格式】
如果存在三个人构成“剪刀石头布”局面,输出三个人的编号(从 1 开始)。
如果不存在这样的三个人,输出一个数-1。
【样例输入】
5
00100
10000
01001
11101
11000
【样例输出】
1 3 2
【数据规模与约定】
55。
80%的数据,1 ≤ 푁 ≤ 10。

对于100%的数据,1 ≤ 푁 ≤ 5000



分析:

理论上这是一个竞赛图,竞赛图有一个重要的性质,就是如果存在一个环(包含两个以上结点的环),就一定存在一个三元环,即题意的石头剪刀布局面。

下面来证明此性质:

假设1->2,2->3,3->4,4->5,5->1,我们来证明存在三元环。

因为1->2,2->3,所以如果3->1,就一定会存在三元环,

如果3不能打败1,那么就是1打败3,这就是竞赛,所以有1->3,那么这时环中就减少了一个结点,

同理,我们还可以一直减少结点,那么最终一定会得到三元环。

至于有向图找环,那就是求强连通分量。

tarjan求环见此。


参考程序:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=5100;
const int maxm=5100;
char tmp[maxn];
int cycle[maxn],stack[maxn],dfn[maxn],spos[maxn];
int a[maxm][maxm];
int top=0,Time=0,n,len=0;
bool tarjan(int u){
	dfn[u]=1;
	stack[top++]=u;
	spos[u]=top-1;
	for (int i=1;i<=n;i++)
		if (a[u][i]){
			if (dfn[i]==1){
				len=top-spos[i];
				memcpy(cycle,stack+spos[i],sizeof(int)*len);
				return true;
			}else if (!dfn[i]){
				bool ok=tarjan(i);
				if (ok)return true;
			}
		}
	top--;dfn[u]=2;
	return false;
}
void print(){
	for (int i=1;i<len-1;i++)
		if (a[cycle[i+1]][cycle[0]])
			printf("%d %d %d\n",cycle[0],cycle[i],cycle[i+1]);
}
int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		scanf("\n%s",tmp+1);
		for (int j=1;j<=n;j++)
			a[i][j]=tmp[j]=='1';
	}
	for (int i=1;i<=n;i++)
		if (!dfn[i])
			if (tarjan(i)){
				print();
				return 0;
			}
	printf("-1");
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值