UVALive 7334 Kernel Knights (dfs)

Kernel Knights

题目链接:

http://acm.hust.edu.cn/vjudge/contest/127407#problem/K

Description


Jousting is a medieval contest that involves people on horseback trying to strike each other with wooden
lances while riding at high speed. A total of 2n knights have entered a jousting tournament — n knights
from each of the two great rival houses. Upon arrival, each knight has challenged a single knight from
the other house to a duel.
A kernel is defined as some subset S of knights with the following two properties:
• No knight in S was challenged by another knight in S.
• Every knight not in S was challenged by some knight in S.
Given the set of the challenges issued, find one kernel. It is guaranteed that a kernel always exists.

Input


The input file contains several test cases, each of them as described below.
The first line contains an integer n (1 ≤ n ≤ 100000) — the number of knights of each house. The
knights from the first house are denoted with integers 1 through n, knights from the second house with
integers n + 1 through 2n.
The following line contains integers f1, f2, . . ., fn — the k-th integer fk is the index of the knight
challenged by knight k (n + 1 ≤ fk ≤ 2n).
The following line contains integers s1, s2 , . . ., sn — the k-th integer sk is the index of the knight
challenged by knight n + k (1 ≤ sk ≤ n).

Output


For each case, output the indices of the knights in the kernel on a single line. If there is more than one
solution, you may output any one.

Sample Input


4
5 6 7 7
1 3 2 3

Sample Output


1 2 4 8


题意:


有两队骑士各n人,每位骑士会挑战对方队伍的某一个位骑士. (可能相同)
要求找出一个集合S,使得:(任意满足条件即可)
集合S中的骑士不会互相挑战.
每个集合外的骑士必定会被集合S内的某个骑士挑战.


题解:


一开始看题有点懵比,题目的两层要求绕得有点糊涂.
在模拟样例的过程中发现,有些点是必须在S中的,而有些必须在S外,有些是不固定的.
首先,如果某个骑士没有被人挑战,那么他一定要位于S中. (反之他在集合外的话,就违背了条件2).
然后,如果某个骑士被确定在S中时,那么他的挑战对象一定要在S外. (反之违背条件1).
若某个骑士i被多个人挑战,那么要先对这些挑战者逐一进行上述判断,若某个挑战者被确定在S外,那么说明能使骑士i满足条件2的挑战者少了一个(等同于少了一个挑战者). 若所有挑战者都在S外,那么i一定在S内.


一开始觉得上述条件不够充分,特别是存在多个挑战者时,考虑会不会存在某个挑战者无法确定而导致i确定不了.
考虑无法确定的情况:
首先一定是成对出现,若只出现一个,那么由上述判据一定能够确定它.
比如样例中的1<->5(互相挑战),上述判据就无法确定. 这时候可以推断1和5只要任意一个在集合S内都满足情况.


直接用dfs或bfs搜状态即可,从入度为0的点开始,若某点的父结点被确定在S外,则将该点的入度减少1.


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <list>
#define LL long long
#define eps 1e-8
#define maxn 201000
#define mod 100000007
#define inf 0x3f3f3f3f
#define mid(a,b) ((a+b)>>1)
#define IN freopen("in.txt","r",stdin);
using namespace std;

int reach[maxn];
int mp[maxn];
bool vis[maxn];
int ans[maxn];

void dfs(int cur) {
    vis[cur] = 1;
    if(vis[mp[cur]]) return;
    if(ans[cur] == 1) { // in;
        ans[mp[cur]] = -1;
        dfs(mp[cur]);
        return;
    }

    reach[mp[cur]]--; // out;
    if(!reach[mp[cur]]) {
        ans[mp[cur]] = 1;
        dfs(mp[cur]);
    }
}

int main(int argc, char const *argv[])
{
    //IN;

    int n;
    while(scanf("%d", &n) != EOF)
    {
        memset(reach, 0, sizeof(reach));
        for(int i=1; i<=2*n; i++) {
            int x; scanf("%d", &x);
            mp[i] = x;
            reach[x]++;
        }

        memset(vis, 0, sizeof(vis));
        memset(ans, 0, sizeof(ans));
        for(int i=1; i<=2*n; i++) {
            if(!vis[i] && !reach[i]) {
                ans[i] = 1;
                dfs(i);
            }
        }

        vector<int> p; p.clear();
        for(int i=1; i<=2*n; i++) {
            if(ans[i] == -1) continue;
            if(ans[i] == 1) p.push_back(i);
            else if(i <= n) p.push_back(i);
        }
        int sz = p.size();
        for(int i=0; i<sz; i++) {
            printf("%d%c", p[i], i==sz-1?'\n':' ');
        }
    }

    return 0;
}

转载于:https://www.cnblogs.com/Sunshine-tcf/p/5758052.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值