博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tarjan算法初探(2):缩点
阅读量:4610 次
发布时间:2019-06-09

本文共 2740 字,大约阅读时间需要 9 分钟。

接上一节 

 

Tarjan算法一个非常重要的应用就是 在一张题目性质在点上性质能够合并的普通有向图中将整个强连通分量视作一个点来把整张图变成一张DAG(即有向无环图) 而DAG的形态满足最优子结构经常与DP联系在一起 故缩点常作为一条桥梁将图论DP相联系

缩点思想不难理解 这里主要说明一下代码的操作细节与流程:

1.使用Tarjan算法求出每个点属于哪一个强连通分量

2.枚举每一条点将每一个点对应性质合并到新的点上

3.枚举每一条边将每条边两端的点对应的新点相连来重构整张图(注意自环的问题) 重构时可能有的重边不影响重构图是一个DAG

其中 2 3 步可同时进行。

下面贴一道模板题来熟悉代码

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。 n<=10^4,m<=10^5,0<=点权<=1000

分析:

裸题 缩点以后直接DP完事 没有思维难度的DP

1 #include 
2 #include
3 #define re register 4 #define r(x) x=read() 5 #define MAXX 10005 6 #define MIN(a,b) (a
b?a:b) 8 using namespace std; 9 int dfn[MAXX],low[MAXX],que[MAXX],ans[MAXX],w[MAXX],w2[MAXX],id[MAXX],10 st[MAXX],top,u,to,n,m,cnt[2],k,num,h[2][MAXX],sum[MAXX];11 struct edge{
int to,nex;}e[2][MAXX<<3];12 void add(int u,int to,int id)13 {14 ++cnt[id];15 e[id][cnt[id]]=(edge){to,h[id][u]};16 h[id][u]=cnt[id];17 }18 int read()19 {20 char ch=0;int w=0,ff=1;21 while(ch<'0'||ch>'9'){
if(ch=='-')ff=-1;ch=getchar();}22 while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}23 return ff*w;24 }25 void tarjan(int now)//求强连通分量 26 {27 low[now]=dfn[now]=++k;28 st[++top]=now;29 for(int i=h[0][now];i;i=e[0][i].nex)30 {31 if(!dfn[e[0][i].to])32 tarjan(e[0][i].to),low[now]=MIN(low[now],low[e[0][i].to]);33 else 34 if(!id[e[0][i].to])35 low[now]=MIN(low[now],dfn[e[0][i].to]);36 }37 if(low[now]==dfn[now])38 {39 id[now]=++num;40 while(st[top]!=now)41 {42 id[st[top]]=num;43 --top;44 }45 --top;46 }47 }48 void build()//缩点 49 {50 for(int i=1;i<=n;++i)51 {52 w2[id[i]]+=w[i];//点权值合并 53 for(int j=h[0][i];j;j=e[0][j].nex)54 if(id[i]!=id[e[0][j].to]) 55 add(id[i],id[e[0][j].to],1),++sum[id[e[0][j].to]];//连边 56 }57 for(int i=1;i<=num;++i)58 if(sum[i]==0) que[++top]=i,ans[que[top]]+=w2[que[top]];59 for(int i=1;i<=top;++i)60 {61 for(int j=h[1][que[i]];j;j=e[1][j].nex)62 {63 sum[e[1][j].to]--;64 if(!sum[e[1][j].to]) que[++top]=e[1][j].to;65 }66 }//拓扑排序 67 }68 void solve()//裸DP 69 {70 for(int i=1;i<=top;++i)71 for(int j=h[1][que[i]];j;j=e[1][j].nex)72 ans[e[1][j].to]=MAX(ans[e[1][j].to],w2[e[1][j].to]+ans[que[i]]);73 int maxx=0;74 for(int i=1;i<=num;++i)75 maxx=max(maxx,ans[i]);76 printf("%d",maxx);77 }78 void input()79 {80 r(n),r(m);81 for(re int i=1;i<=n;++i)82 r(w[i]);83 for(re int i=1;i<=m;++i)84 r(u),r(to),add(u,to,0);85 }86 int main()87 {88 input();89 for(re int i=1;i<=n;++i)90 if(!id[i])91 tarjan(i);92 build();93 solve();94 return 0;95 }

 

转载于:https://www.cnblogs.com/dah1314/p/10698826.html

你可能感兴趣的文章
数据库的创建和删除
查看>>
最简单的三层实例【插入据
查看>>
设计模式学习笔记——Prototype原型模式
查看>>
pom.xml里有红叉报错的解决办法
查看>>
Perl last和next的用法区别
查看>>
Selenium 管理 Cookies
查看>>
exceptionfunction[LeetCode]Permutations
查看>>
Linux(2)_常用命令2
查看>>
自定义分页
查看>>
[转]DELPHI——调试(1)
查看>>
JS秒数转成分秒时间格式
查看>>
xp_cmdshell 命令的开启与关闭,和状态查询
查看>>
Linux sudoers
查看>>
MySQL详解(18)-----------分页方法总结
查看>>
bzoj 4595 激光发生器
查看>>
multi cookie & read bug
查看>>
js时间转换
查看>>
(转载) Android Studio你不知道的调试技巧
查看>>
队列实现霍夫曼树
查看>>
JAVA 笔记(一)
查看>>