【nowcoder 通知小弟】(tarjan缩点)

链接:https://www.nowcoder.com/acm/contest/76/E
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
在战争时期,A国派出了许多间谍到其他国家去收集情报。因为间谍需要隐秘自己的身份,所以他们之间只是单向联系。所以,某个间谍只能单向联系到一部分的间谍。同时,间谍也不知道跟他联系的是谁。
HA是间谍们的老大,但他也只能联系到部分的间谍。HA现在有一项命令有告诉所有的间谍。HA想要知道他至少要告诉多少个他能联系上的间谍才能通知到所有的间谍。
输入描述:
有多个测试数据。
对于每个测试数据:
第一行为一个整数n,m(0<n,m<=500)代表间谍的数量和HA能通知到的间谍的数量(间谍的编号为1-n);
第二行为m个用空格隔开的整数xi,代表HA能通知到的间谍的编号;
第三行到第n+2行,每一行第一个整数ai(0<=ai<n)表示第i-2个间谍能单向联系到的间谍数。之后有ai个用空格隔开的整数,表示间谍i-2能单向联系到的间谍的编号。
输出描述:
输出一行,此行中有一个整数,代表HA至少需要联系的间谍数。如果HA不能通知到所有间谍,输出-1。
示例1
输入
3 2
1 2
1 2
1 1
0
输出
-1
示例2
输入
3 1
1
2 2 3
0
0
输出
1

分析: 赛时没写出来,想到的方法是tarjan,但是用的不够熟练,所以没出。
赛后,各种神仙代码。。 数据太弱了,所以 并查集也能过

比较容易想到的是 如果是DAG,只需要通知到那个入度为0的结点的间谍就可以了,但如果存在环,也只需要通知到其中的一个就可以,但是并查集就不能解决。。 数据弱了,样例没过都能A OoO

所以用 tarjan缩点 找入度为0的点并判断是否能通知到该间谍

【tarjan 缩点】
AC代码:

#include <bits/stdc++.h>
using namespace std;

#define mem(a,n) memset(a,n,sizeof(a))
#define memc(a,b) memcpy(a,b,sizeof(b))
#define rep(i,a,n) for(int i=a;i<n;i++) ///[a,n)
#define dec(i,n,a) for(int i=n;i>=a;i--)///[n,a]
#define pb push_back
#define fi first
#define se second
#define IO ios::sync_with_stdio(false)
#define fre freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double PI=acos(-1.0);
const double E=2.718281828459045;
const double eps=1e-3;
const int INF=0x3f3f3f3f;
const int MOD=258280327;
const int N=5e2+5;
const ll maxn=1e6+5;
const int dir[4][2]= {-1,0,1,0,0,-1,0,1};
int color[N],vis[N],sta[N];
int ans[N],dfn[N],low[N];
int n,m,scc,cnt,tail;
int in[N];///入度
vector<int>g[N];
void init()
{
    cnt=1;
    scc=0;///强连通分量个数
    tail=0;
    mem(in,0);
    mem(low,0);
    mem(dfn,0);
    mem(vis,0);
    rep(i,0,n+1) g[i].clear();
}
void tarjan(int u)
{
    vis[u]=1; ///访问过标记为1
    low[u]=dfn[u]=cnt++;
    sta[++tail]=u;///入栈
    for(auto v:g[u])
    {
        if(vis[v]==0)///结点v没有访问过
            tarjan(v);
        if(vis[v]==1)///结点v访问过
            low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u]) ///一个强联通分量的判断
    {
        scc++;
        do///缩点
        {
            low[sta[tail]]=scc;///更新low数组的值
            color[sta[tail]]=scc;///缩点染色
            vis[sta[tail]]=-1;///染色标记
        }
        while(sta[tail--]!=u);///一直出栈,直到回到最初的点
    }
}
int a[N];
void solve()
{
    rep(i,1,n+1)
    if(!vis[i])
        tarjan(i);
    rep(i,1,n+1)
    {
        for(auto v:g[i])
        {
            if(color[i]!=color[v])
                in[color[v]]++;
        }
    }
    int res=0;
    bool flag=0;
    rep(i,1,scc+1)///遍历所有的连通分量
    {
        if(!in[i])///结点i入度为0
        {
            flag=0;///能否通知到间谍i的标记
            rep(j,1,n+1)
            {
                if(color[j]==i&&binary_search(a,a+m,j))
                {
                    res++;
                    flag=1;
                    break;
                }
            }
            if(!flag)///不能通知到间谍i
                break;
        }
    }
    printf("%d\n",flag?res:-1);
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        rep(i,0,m) scanf("%d",&a[i]);
        sort(a,a+m);
        rep(i,1,n+1)
        {
            int x,y;
            scanf("%d",&x);
            while(x--)
            {
                scanf("%d",&y);
                g[i].pb(y);
            }
        }
        solve();
    }
    return 0;
}

蒻苣,以下代码 可略过~
错误代码:
【并查集】

#include <bits/stdc++.h>
using namespace std;

#define mem(a,n) memset(a,n,sizeof(a))
#define memc(a,b) memcpy(a,b,sizeof(b))
#define rep(i,a,n) for(int i=a;i<n;i++) ///[a,n)
#define dec(i,n,a) for(int i=n;i>=a;i--)///[n,a]
#define pb push_back
#define fi first
#define se second
#define IO ios::sync_with_stdio(false)
#define fre freopen("in.txt","r",stdin)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long ll;
typedef unsigned long long ull;
const double PI=acos(-1.0);
const double E=2.718281828459045;
const double eps=1e-3;
const int INF=0x3f3f3f3f;
const int MOD=258280327;
const int N=5e2+5;
const ll maxn=1e6+5;
const int dir[4][2]= {-1,0,1,0,0,-1,0,1};
int par[N],rrank[N];
int a[N];
void init(int n)
{
    rep(i,0,n+1) par[i]=i;
}
int Find(int x)///找根
{
    return par[x]==x?x:Find(par[x]);
}
void unite(int a,int b)
{
    int c,d;
    c=Find(a);
    d=Find(b);
    if(c==d) return ;
    if(rrank[c]<rrank[d])
        par[c]=d;
    else
    {
        par[d]=c;
        if(rrank[c]==rrank[d])
            rrank[c]++;
    }
}
int main()
{
   // fre;
    int n,m;
    while(cin>>n>>m&&(n+m))
    {
        mem(rrank,0);
        mem(a,0);
        init(n);
        rep(i,1,m+1) cin>>a[i];
        rep(i,1,n+1)
        {
            int x,y;
            cin>>x;
            rep(j,0,x)
            {
                cin>>y;
                unite(y,i);
            }
        }
        set<int>st;
        rep(i,1,m+1)
        {
            if(a[i]==par[a[i]])
                st.insert(a[i]);
        }
        bool flag=1;
        rep(i,1,n+1)
        {
            if(i==par[i]&&st.find(i)==st.end())
            {
                flag=0;
                break;
            }
        }
        if(flag) cout<<st.size()<<endl;
        else cout<<-1<<endl;;
        st.clear();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值