缩点 洛谷P3387
tarjan算法中使用了dfn和low两个数组,他们分别是dfs时这个点是第几个被搜索到的和这个点能走到的dfn最小的点的值,通过这两个数组加上一个stack去记录他们的父子关系,便可以完成搜点这项工作
代码
#include <iostream>
#include<cstdio>
#include<queue>
using namespace std;
int p[10005];
struct node
{
int from,to,next;
}edge[100005],ed[100005];
int head[100005],sum,s,h[100005];
int stac[100005],top,vis[100005];
int dnf[100005],low[100005],tim;
int sd[100005],in[100005];
int n,m;
int dp[100005];
void add(int u,int v)
{
edge[++sum].next=head[u];
edge[sum].from=u;
edge[sum].to=v;
head[u]=sum;
}
void tarjan(int cur)
{
//tim++;
dnf[cur]=low[cur]=++tim;
stac[++top]=cur;
vis[cur]=1;
for(int i=head[cur];i>0;i=edge[i].next)
{
int v=edge[i].to;
if(!dnf[v])
{
tarjan(v);
low[cur]=min(low[v],low[cur]);
}
else if(vis[v])
low[cur]=min(dnf[v],low[cur]);
}
if(dnf[cur]==low[cur])
{
int y;
while(1)
{
y=stac[top--];
sd[y]=cur;
vis[y]=0;
if(cur==y)
break;
p[cur]+=p[y];
}
}
}
int topo()
{
queue<int> qu;
for(int i=1;i<=n;i++)
{
if(sd[i]==i&&!in[i])
{
qu.push(i);
dp[i]=p[i];
}
}
while(!qu.empty())
{
int now=qu.front();
qu.pop();
for(int i=h[now];i>0;i=ed[i].next)
{
int v=ed[i].to;
dp[v]=max(dp[v],dp[now]+p[v]);
in[v]--;
if(!in[v])
qu.push(v);
}
}
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,dp[i]);
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>p[i];
for(int i=0;i<m;i++)
{
int u,v;
cin>>u>>v;
add(u,v);
}
for(int i=1;i<=n;i++)
if(!dnf[i])
tarjan(i);
for(int i=1;i<=m;i++)
{
int u,v;
u=sd[edge[i].from];
v=sd[edge[i].to];
if(u!=v)
{
ed[++s].next=h[u];
ed[s].from=u;
ed[s].to=v;
h[u]=s;
in[v]++;
}
}
//printf("m:%d sum:%d\n",m,sum);
int ans=topo();
cout<<ans<<endl;
return 0;
}
P3388 题解
同样是利用tarjan算法,只要找到low[v]>=dfn[u]的点,那么u便为割点,这是u不为起始节点的情况(就是dfs开始的节点),如果u为开始节点,那么只要有两个以上的孩子,便为割点。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<sstream>
#include<string>
using namespace std;
struct node
{
int from,to,next;
}egde[1000005];
int head[1000005],sum=0;
int dfn[100005],low[100005],tim;
int cut[100005];
void add(int u,int v)
{
egde[++sum].next=head[u];
egde[sum].from=u;
egde[sum].to=v;
head[u]=sum;
}
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++tim;
int child=0;
for(int i=head[u];i;i=egde[i].next)
{
int v=egde[i].to;
if(!dfn[v])
{
tarjan(v,fa);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]&&u!=fa)
cut[u]=1;
if(u==fa)
child++;
}
low[u]=min(low[u],dfn[v]);
}
if(child>=2&&u==fa)
cut[u]=1;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++)
{
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i,i);
int ans=0;
for(int i=1;i<=n;i++)
if(cut[i])
ans++;
cout<<ans<<endl;
int flag=1;
for(int i=1;i<=n;i++)
{
if(cut[i])
{
if(flag)
{
cout<<i;
flag=0;
}
else
cout<<" "<<i;
}
}
cout<<endl;
return 0;
}