您的位置:首页 > 其它

2018年全国多校算法寒假训练营练习比赛(第四场)-E-通知小弟【强联通】

2018-02-16 20:06 375 查看
题意:

第一行:n个点 m组数据

第二行:HA能通知到的人

第三行——最后一行(1———n):【第一个字表示有几个能通知到的】

思路:

强联通分量个数

缩点后处理

输入

3 2

1 2

1 2

1 1

0

输出

-1

输入

3 1

1

2 2 3

0

0

输出

1

#include<stdio.h>
#include<string.h>
#include<stack>
#include<vector>
#include<iostream>
using namespace std;

#define MAXN 505

int n,m;
///dfn记录节点的被访问时间(时间戳)
///low记录该点或者以这个点为根的子树能够追溯到最早的栈中节点的次序
int dfn[MAXN],low[MAXN];
///vis标记是否在栈中,num为每个强连通分量的节点数,belong标记节点属于第几个强连通分量
int vis[MAXN],num[MAXN],belong[MAXN];
int cnt,idx; ///cnt为强连通分量的个数,idx为节点访问的编号(每个节点不同)
stack<int> s;
vector<int> map[MAXN];

void tarjan(int u)
{
int i,v;
dfn[u]=low[u]=++idx; ///新点初始化
s.push(u); ///入栈
vis[u]=1;  ///标记在栈中
for(i=0; i<map[u].size(); i++)
{
///处理与u相邻的节点
v=map[u][i];
if(!dfn[v])
{
///如果没被访问过,不能用!vis[v],vis表示是否在栈中
tarjan(v); ///递归处理
low[u]=min(low[v],low[u]); ///将强连通分量的所有节点low值改为根节点的dfn值
}
///如果访问过,并且在栈中,表明存在环
else if(vis[v]&&dfn[v]<low[u]) low[u]=dfn[v];
}
if(dfn[u]==low[u])
{
///如果是强连通分量子树的最小根,此时栈内u之上的元素构成起一个强连通分量
cnt++; ///第cnt个强连通分量
do
{
///输出 也可以开个vector数组存储一下
v=s.top();
s.pop();
vis[v]=0; ///标记不在栈中
belong[v]=cnt; ///节点v属于第cnt个强连通分量
num[cnt]++;
}
while(u!=v);
}
}

void solve()
{
int i;
cnt=0;
idx=0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
memset(num,0,sizeof(num));
for(i=1; i<=n; i++) ///如果没被访问过则从该点开始处理,防止有点遗漏
if(!dfn[i]) tarjan(i);
}

vector<int> mp[MAXN];   ///由所有强连通分量形成的新图
int ru[MAXN]; //入度

void suodian(int n)
{
///缩点函数,其中n为节点个数
int i,j,u,v;
memset(ru,0,sizeof(ru));
for(i=1; i<=n; i++) mp[i].clear();
for(i=1; i<=n; i++)
{
///扫描每个节点
u=belong[i];   ///原来起点所在的强连通分量
for(j=0; j<map[i].size(); j++)
{
///每个节点的相邻节点
v=belong[map[i][j]]; ///原来终点所在的强连通分量
if(u!=v)
{
///如果不在同一强连通分量内
mp[u].push_back(v); ///形成新图
ru[v]++;
}
}
}
}

int main()
{
int i,j,x,f,sum,ans,a[505],mark[505];
while(~scanf("%d%d",&n,&m))
{
for(i=0; i<=n; i++) map[i].clear();
for(i=0; i<m; i++) scanf("%d",&a[i]);
for(i=1; i<=n; i++)
{
scanf("%d",&sum);
for(j=0; j<sum; j++)
{
scanf("%d",&x);
map[i].push_back(x);
}
}
solve();
suodian(n);
memset(mark,0,sizeof(mark));
///将能通知到的人所在的强联通分量标记
for(i=0; i<m; i++) mark[belong[a[i]]]=1;
f=1;
ans=0;
///printf("cnt=%d\n",cnt);
for(i=1; i<=cnt; i++)
{
///
if(!mark[i]&&!ru[i])
{
///如果有人没通知到且没人通知的到
f=0;
break;
}
///如果此人没人通知的到并且间谍老大可以通知到
else if(!ru[i]&&mark[i]) ans++;
}
if(!f) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐