http://poj.org/problem?id=1236
思路:第一问的话其实还有其他的方法可做。那道题叫做刻录光盘,通过并查集和统计度数来完成。P2835 刻录光盘(并查集建单向边)
这道题的思路:首先tarjan缩点。缩点缩完了后看新图之间有多少个入度为0的点,这就是最后要给的作为起始的点。
对于第二问,缩点完后考虑几种情况会发现:
1-->2<----3
^
|
4
2<----1------>3
|
V
4
分别要在入度为0和出度为0中选一个最大的。
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<stack>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e2+100;
typedef long long LL;
vector<LL>g[maxn];
stack<LL>s;
LL dfn[maxn],low[maxn],times=0,cnt=0,in[maxn],out[maxn];
bool inq[maxn];
LL col[maxn];///缩点
void tarjan(LL x)
{
dfn[x]=low[x]=++times;
s.push(x);inq[x]=true;
for(LL i=0;i<g[x].size();i++)
{
LL to=g[x][i];
if(!dfn[to])
{
tarjan(to);
low[x]=min(low[x],low[to]);
}
else if(inq[to])
{
low[x]=min(low[x],dfn[to]);
}
}
if(dfn[x]==low[x])
{
cnt++;
LL y;
do
{
y=s.top();
inq[y]=false;
col[y]=cnt;
s.pop();
}while(y!=x);
return;
}
}
int main(void)
{
LL n;cin>>n;
for(LL i=1;i<=n;i++)
{
LL to;
while(cin>>to&&to!=0)
{
g[i].push_back(to);
}
}
for(LL i=1;i<=n;i++)
{
if(!dfn[i]) tarjan(i);
}
if(cnt==1) cout<<1<<endl<<0<<endl;
else
{
for(LL i=1;i<=n;i++)
{
for(LL j=0;j<g[i].size();j++)
{
if(col[i]!=col[g[i][j]]) in[col[g[i][j]]]++,out[col[i]]++;
}
}
LL ans1=0;LL ans2=0;
for(LL i=1;i<=cnt;i++)
{
if(in[i]==0) ans1++;
if(out[i]==0) ans2++;
}
cout<<ans1<<endl<<max(ans1,ans2)<<endl;
}
}