用于求解强连通分量相关问题
在DAG图中
对于一个点x有low[ ] (用于记录该点所在强连通分量的入口位置),
dfn[ ](用于记dfs时重新编号遍历之后的序号),
初始值low=dfn
另有point[ ] 用于记录一个强连通分量(新点)
pw[ ] 记录各新点(总)权值
开一个栈stack
每当遍历到一个点时将其加入栈
并扫描所有当前点连接的点
如果!dfn[ ] 即没有被访问过
那么进入该点并在返回时更新当前点low值(后向边√)
否则检查该点是否在stack内
如果不在stack内但是dfn不是0
说明这个点已经被访问过并且属于另外一个强连通分量(横叉边×)
如果在的话更新当前点low值
当进行完上述操作后 如果这个时候这个点的low值还没有被更新
即 low == dfn
那么说明这个点就是他所在强连通分量的一个入口点
直接退栈 直到把这个点退掉
然后这次从栈里退出来这些点就和当前点属于一个强连通分量
退栈时顺便就加到point里面然后把这些点的权值加到pw里面了
对于新的点point需要重新建图
自己想的一种做法是:
退栈的时候顺便遍历一遍退掉的这些点连接的其他point里面的点
然鹅看网上代码大多都是dfs完之后
再访问用结构体(或者vector)记录的原图边
每条边都有(u,v)两个点
如果point[u]!=point[v] 那么就add(point[a],point[b])到新图中
所以还是从众了。。
luogu P3378 缩点:
const int maxn=1e5+10;
struct rec {
int f,t,next;
}e[maxn],ee[maxn];
int now[maxn],w[maxn],p,pp,noww[maxn];
void add(int x,int y)
{
p++;
e[p].f=x;
e[p].t=y;
e[p].next=now[x];
now[x]=p;
}
void addd(int x,int y)
{
pp++;
ee[pp].f=x;
ee[pp].t=y;
ee[pp].next=noww[x];
noww[x]=pp;
}
int dfn[maxn],low[maxn],stack[maxn],top,cnt,point[maxn],pcnt,pw[maxn],dp[maxn];
bool instack[maxn];
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
instack[x]=1;
stack[++top]=x;
int ed=now[x];
while(ed)
{
int v=e[ed].t;
if(!dfn[v])
{
tarjan(v);
low[x]=min(low[x],low[v]);// 打的时候这里写成low[v]=...了
}
else if(instack[v])low[x]=min(dfn[v],low[x]);
ed=e[ed].next;
}
if(dfn[x]==low[x])
{
int t=top;
int y=stack[top];
pcnt++;
{
y=stack[top--];
pw[pcnt]+=w[y];
//cout<
instack[y]=0;
point[y]=pcnt;
} while(x!=y);//这里要用do while
/* while(x!=y)
{
y=stack[t--];
int ed=now[y];
int v=e[ed].t;
while(ed)
{
if(point[v]!=pcnt) addd(pcnt,point[v]);
ed=ee[ed].next;
}
}*/ //另一种加边方法
}
}
int dfs(int x)
{
if(dp[x])return dp[x];
int ed=noww[x];
int ans=0;
while(ed)
{
int v=ee[ed].t;
ans=max(dfs(v),ans);
ed=ee[ed].next;
}
return dp[x]=ans+pw[x];
}
int main()
{
//ios::sync_with_stdio(false);
int n=read(),m=read();
for(int i=1;i<=n;i++)
{
w[i]=read();
}
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
add(x,y);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])tarjan(i); // tarjan缩点 把全部点遍历防止疏漏
}
int ans=0;
for(int i=1;i<=n;i++)
{
int ed=now[i];
while(ed)
{
if(point[i]!=point[e[ed].t])
{
addd(point[i],point[e[ed].t]); //注意这里是加的point[ ]
}
ed=e[ed].next;
}
}
for(int i=1;i<=pcnt;i++)
{
if(!dp[i]){
ans=max(dfs(i),ans);
} // DAG图上的dp
}
cout<return 0;
}
一些小细节
弹栈的时候一定要用do while 确保把low == dfn 的点也弹出
可以通过加add函数的参数实现两种加边 不用再写两个函数
一定把点都tarjan一遍防止疏漏
细心细心再细心