noip模拟赛 戏

【问题背景】
zhx 和他的妹子(们) 做游戏。
【问题描述】
考虑 N 个人玩一个游戏,任意两个人之间进行一场游戏(共 N*(N-1)/2 场),
且每场一定能分出胜负。
现在, 你需要在其中找到三个人构成剪刀石头步局面: 三个人 ABC
满足 A 战胜 BB 战胜 CC 战胜 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
【数据规模与约定】

分析:今天才知道这种图叫竞赛图,还是第一次接触它.

      首先,竞赛图中是没有两元环的,两个点不可能互指.并且竞赛图中一条任意长度的环都可以通过某种变换变成3元环,也就是题目要求的.具体是怎么变换呢?考虑这样一个环:

考虑前3个点1、2、3,如果1、3之间的连边是从3指向1,那么就找到了一个3元环,否则,我们完全可以不考虑2这个点,继续考虑1,3,4,检查1,4的连边,这就和1,2,3这种情况一样了,每次把环的长度-1,由于不存在2元环,所以最后一定可以找到一个三元环.

    接下来的任务就是怎么要找到一个任意长度的环.

把dfs走的图看作一棵“树”,如果子节点有边连向祖先,那么就形成了环.将dfs时的vis数组改一下就可以了,vis[i] = 0表示i没有访问过,i=1表示正在访问i或者i的子树,i=2表示i的子树访问完了.如果下一个点vis=1,那么就找到一个环了.维护一个栈,记录一个时间戳,访问到新节点就加入到栈中,子树访问完了就退出来,找到环后就把栈中对应下标的元素一复制就好了.

    一个比较重要的思想是把其它的所有不满足条件的情况都给排除掉,一步步接近我们的目标.就好比找3元环,首先证明2元环是不存在的,然后证明大于3元环的一定可以找到3元环,把问题一步步缩小就能找到答案.

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

using namespace std;

int n, a[5010][5010], dfs_clock, s[5010], time[5010], head[5010], to[5010 * 5010], nextt[5010 * 5010], tot = 1;
int vis[5010], top, ans[5010], cnt;

bool flag = false;

void add(int x, int y)
{
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;
}

void dfs(int u)
{
    if (flag)
        return;
    vis[u] = 1;
    s[++top] = u;
    time[u] = top;
    for (int i = head[u]; i; i = nextt[i])
    {
        int v = to[i];
        if (!vis[v])
            dfs(v);
        if (vis[v] == 1)
        {
            flag = 1;
            for (int j = time[v]; j <= top; j++)
                ans[++cnt] = s[j];
            return;
        }
    }
    vis[u] = 2;
    s[top--] = 0;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
        {
            scanf("%1d", &a[i][j]);
            if (a[i][j])
                add(i, j);
        }
    for (int i = 1; i <= n; i++)
        if (!vis[i])
        {
            dfs(i);
            if (flag)
                break;
        }
    if (!flag)
        printf("-1");
    else
    {
        for (int i = 2; i < cnt; i++)
            if (a[s[i + 1]][s[1]])
            {
                printf("%d %d %d\n", s[1], s[i], s[i + 1]);
                break;
            }
    }

    return 0;
}

 

posted @ 2017-10-26 16:04  zbtrs  阅读(278)  评论(0编辑  收藏  举报