tarjian算法求强连通分量网上有很多博客写了,并且这个东西一个人一个打法,还是个人理解了自己打比较好,这里附上我的代码~
void dfs(int u)
{
low[u] = pre[u] = ++dfs_clock;
s.push(u);
for(int i = head[u];i;i = next[i])
{
int v = edges[i].to;
if(!pre[v])
{
dfs(v);
low[u] = min(low[u],low[v]);
}
else if(!sccnum[v]) low[u] = min(low[u],pre[v]);
}
if(low[u] == pre[u])
{
++scc_cnt;
db[scc_cnt] = u;
while(233)
{
int f = s.top(); s.pop();
sccnum[f] = scc_cnt;
if(f == u) break;
}
}
}
有没有注意我的tarjian里多了一个数组,叫做db,它是什么意思呢?
db的意思是这个强连通分量的代表节点,也就是缩点。
缩点,顾名思义,把一个强连通缩成一个点,当访问强连通分量内的点时直接访问这个代表节点,并且将原先强连通分量里的点连出的边连到这个代表节点上,以后每次访问强连通分量内的任何一个点都访问代表节点,就可以实现缩点了。
是不是很晕?还是拿例题来讲:——–>链接
这个题是个不错的模板题呢~相信你会了这个题就学会了缩点~
我们先分析一下,如果有环的情况下,肯定是把一条边的所有蘑菇都榨取完才是最优解(说好的要守护大自然保护大自然呢~),于是就想到了:tarjian缩点,把一个强连通的价值求出来,然后缩成一个点,跑最长路,这是这个模板题的基本思路,接下来我想用代码来讲一下具体实现……
1、先用tarjian求一下强连通,朴素的tarjian即可。
void dfs(int u)
{
low[u] = pre[u] = ++dfs_clock;
s.push(u);
for(int i = head[u];i;i = next[i])
{
int v = edges[i].to;
if(!pre[v])
{
dfs(v);
low[u] = min(low[u],low[v]);
}
else if(!sccnum[v]) low[u] = min(low[u],pre[v]);
}
if(low[u] == pre[u])
{
++scc_cnt;
db[scc_cnt] = u;
while(233)
{
int f = s.top(); s.pop();
sccnum[f] = scc_cnt;
if(f == u) break;
}
}
}
2、第二部求得所有强连通分量的价值(价值的定义是一个强连通分量能产出的最大价值),具体过程是:对于每一个强连通分量,遍历所有的强连通分量内部的边,压榨出每条边的价值,让val[now] += ans(val表示编号为now的强连通分量的价值),就实现了求最大价值的过程。(Ps:getvs函数式求强连通分量的价值,getve是求边的价值)
代码:
这是spfa部分,即solve部分:
dfs(S);//上面的dfs就是这里的dfs
for(int i = 1;i <= scc_cnt;i ++)
{
vis[db[i]] = 1;
getvs(db[i],i);
}
下面是getvs部分:
int getve(Edge e) //Edge 获得一条边的最大价值
{
int ans = 0;
int tmp = e.dist;
while(233)
{
ans += tmp;
tmp = (int)((double)(tmp)*e.huif);
if(!tmp) break;
}
return ans;
}
void getvs(int u,int now) //scc_num; 获得当前scc的最大价值(即now的最大价值,它的引用再上面的代码部分)
{
for(int i = head[u];i;i = next[i])
{
int v = edges[i].to;
if(sccnum[v] == now)
{
val[now] += getve(edges[i]);
if(!vis[v])
{
vis[v] = 1;
getvs(v,now);
}
}
}
}
3、重建图,将缩点后的图建出来:
这一步就是去除没用的边重建图,把scc_cnt当做一个点,scc之间建边,得到一张新图,代码易懂,不做赘述~
void newbuild()
{
for(int i = 1;i <= tot;i ++)
{
int f = edges[i].from;
int v = edges[i].to;
if(sccnum[f] != sccnum[v])
{
newedges[++newtot].to = sccnum[v];
newedges[newtot].from = sccnum[f];
newedges[newtot].dist = edges[i].dist;
newedges[newtot].huif = edges[i].huif;
newnext[newtot] = newhead[sccnum[f]];
newhead[sccnum[f]] = newtot;
}
}
}
不过这里要注意是scc之间建边,不再是原先的db的点建边,用db也可以,不过蒟蒻这里还是习惯于用scc的编号建边~
4、跑spfa,得到最终答案即可
最后一步之前已经完成了缩点,这里要对缩点后的图进行操作了,用spfa进行松弛,求最长路即可~,不过要特别注意,所有的与edge有关的都要改成newedges,虽然这里反复提醒,打的时候还是很容易出错的,样例一般都是神样例,甚至你随便打个错误的贪心都能过,所以说还是要在打完这一串的东西之后反复检查没有出现让自己想跳楼的错误,这样才算完满~
q.push(sccnum[S]);
vis[sccnum[S]] = 1;
dist[sccnum[S]] = val[sccnum[S]]; //初始值
while(!q.empty())
{
int f = q.front();
q.pop();
vis[f] = 0;
for(int i = newhead[f];i;i = newnext[i])
{
int v = newedges[i].to;
if(dist[v] < dist[f] + newedges[i].dist + val[v])
{
dist[v] = dist[f] + newedges[i].dist + val[v]; //val[v]
if(!vis[v])
{
vis[v] = 1;
q.push(v);
}
}
}
}
int ans = -1;
for(int i = 1;i <= scc_cnt;i ++)
{
ans = max(ans,dist[i]);
}
return ans;
最后,附上本题完整代码~有疑问希望您留言呢~
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<stack>
using namespace std;
const int size = 500010;
struct Edge{int from,to,dist;double huif;}edges[size],newedges[size];
int head[size],next[size],newnext[size],newhead[size],tot,newtot;
int dfs_clock,scc_cnt,db[size],pre[size],low[size],sccnum[size];
stack <int> s;
void build(int f,int t,int d,double h)
{
edges[++tot].to = t;
edges[tot].dist = d;
edges[tot].from = f;
edges[tot].huif = h;
next[tot] = head[f];
head[f] = tot;
}
void dfs(int u)
{
low[u] = pre[u] = ++dfs_clock;
s.push(u);
for(int i = head[u];i;i = next[i])
{
int v = edges[i].to;
if(!pre[v])
{
dfs(v);
low[u] = min(low[u],low[v]);
}
else if(!sccnum[v]) low[u] = min(low[u],pre[v]);
}
if(low[u] == pre[u])
{
++scc_cnt;
db[scc_cnt] = u;
while(233)
{
int f = s.top(); s.pop();
sccnum[f] = scc_cnt;
if(f == u) break;
}
}
}
bool vis[size];
int val[size];
int getve(Edge e) //Edge
{
int ans = 0;
int tmp = e.dist;
while(233)
{
ans += tmp;
tmp = (int)((double)(tmp)*e.huif);
if(!tmp) break;
}
return ans;
}
void getvs(int u,int now) //scc_num;
{
for(int i = head[u];i;i = next[i])
{
int v = edges[i].to;
if(sccnum[v] == now)
{
val[now] += getve(edges[i]);
if(!vis[v])
{
vis[v] = 1;
getvs(v,now);
}
}
}
}
void newbuild()
{
for(int i = 1;i <= tot;i ++)
{
int f = edges[i].from;
int v = edges[i].to;
if(sccnum[f] != sccnum[v])
{
newedges[++newtot].to = sccnum[v];
newedges[newtot].from = sccnum[f];
newedges[newtot].dist = edges[i].dist;
newedges[newtot].huif = edges[i].huif;
newnext[newtot] = newhead[sccnum[f]];
newhead[sccnum[f]] = newtot;
}
}
}
int S;
queue <int> q;
int dist[size];
int spfa()
{
dfs(S);
for(int i = 1;i <= scc_cnt;i ++)
{
vis[db[i]] = 1;
getvs(db[i],i);
}
newbuild();
memset(vis,0,sizeof(vis));
q.push(sccnum[S]);
vis[sccnum[S]] = 1;
dist[sccnum[S]] = val[sccnum[S]]; //初始值
while(!q.empty())
{
int f = q.front();
q.pop();
vis[f] = 0;
for(int i = newhead[f];i;i = newnext[i])
{
int v = newedges[i].to;
if(dist[v] < dist[f] + newedges[i].dist + val[v])
{
dist[v] = dist[f] + newedges[i].dist + val[v]; //val[v]
if(!vis[v])
{
vis[v] = 1;
q.push(v);
}
}
}
}
int ans = -1;
for(int i = 1;i <= scc_cnt;i ++)
{
ans = max(ans,dist[i]);
}
return ans;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i ++)
{
int a,b,c;
double d;
scanf("%d%d%d%lf",&a,&b,&c,&d);
build(a,b,c,d);
}
scanf("%d",&S);
int ans = spfa();
printf("%d\n",ans);
return 0;
}