###题目
http://codevs.cn/problem/2604/
2604 舞会邀请
时间限制: 1 s
空间限制: 128000 KB
题目描述 Description
Smart是一位颇有成就的艺术家,他因油画作品《我爱bjtam》闻名于世界。现在,他为了报答帮助他的同行们,准备开一个舞会。
Smart准备邀请n个已经确定的人,可是问题来了:
这n个人每一个人都有一个小花名册,名册里面写着他能够通知到的人的名字。比如说在A的人名单里写了B,那么表示A能够通知到B;但是B的名单里不见得有A,也就是说B不见得能够通知到A。
Smart觉得需要确定自己需要通知到多少个人(人数m),能够实际将所有n个人都通知到。并求出一种方案以确定m的最小值是多少。
注意:自己的名单里面不会有自己的名字。
输入描述 Input Description
第一行一个数n(1≤n≤200)。接下来n行,第i+1行表示编号为i的人的小花名册名单,名单以0结束。
输出描述 Output Description
一个整数,即m的值。
样例输入 Sample Input
5 1
2 0
1 3 0
0
0
1 0
样例输出 Sample Output
2
数据范围及提示 Data Size & Hint
1≤n≤200
###题解
建图,tarjian缩环,因为一个环内的所有人只需要通知一个即可
按照sccno重新建图,将图转化为DAG,入度为0的点必须通知,统计答案
期待大爷的for循环做法
###hack
codevs上的某题解:
建立一个无向图,如果第i个人可以通知第j个人,就加入一条i、j之间的无向边,之后每次将结果加一,然后删去任意一个点以及与这个点相连的点,删完后即得到结果。(按照题目描述建立的有向图中的每一组相连的点中至少有一个可以走到其他所以点,所以可以直接建立无向图)
代码短,跑的快。。。
但是是有bug的
5
2 0
3 0
0
3 5 0
0
ans=2
应该至少通知1和4,而他的做法输出答案是1
原因在于他把有向图中的相连的一堆点,丢进无向图中,并且假定只要通知一个点,这一堆点就都能被通知到,显然在上述数据中假定不成立。
codevs上数据真的好水。。。
###代码
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=250;
int dfn[N],low[N],s[N],sccno[N],ru[N];
int cnt_dfn,cnt_scc,top,n,x,ans;
vector<int> mp[N];
void tarjian(int u){
dfn[u]=low[u]=++cnt_dfn;
s[++top]=u;
for(int i=0;i<mp[u].size();i++){
int v=mp[u][i];
if(!dfn[v]){
tarjian(v);
low[u]=min(low[u],low[v]);
}else if(!sccno[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
++cnt_scc;
for(;;){
int x=s[top];
top--;
sccno[x]=cnt_scc;
if(x==u) break;
}
}
return ;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
while(scanf("%d",&x)){
if(x==0) break;
mp[i].push_back(x);
}
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjian(i);
}
for(int i=1;i<=n;i++){
// printf("%d %d\n",i,sccno[i]);
for(int j=0;j<mp[i].size();j++){
int t=mp[i][j];
if(sccno[t]!=sccno[i])
ru[sccno[t]]++;
}
}
for(int i=1;i<=cnt_scc;i++) if(ru[i]==0) ans++;
printf("%d",ans);
return 0;
}