【Tarjan】缩点
给定一个 n 个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
经验
这题使我被迫改变变量命名习惯,因为各种计数变量太多了。。。 不过可读性很高,起码只看代码是能看明白逻辑的
要注意的是,本题思路就是把所有强连通分量当成一个节点,算有向图的最大经过点权
也就是说(我)存在一个疑问:
一个scc向另一个scc连了多于一条边时,难道入度的增加不会影响拓扑排序的结果吗??
我单步调试代码,之后发现了玄妙之处,
拓扑cnt的增加在有点被队列删除时进行,
也就是说:一个点到另一个点多出来的入度会在vector搜边时减去,所以即使有重复的入度,也会在vector操作中被化解影响,也就不影响其他点的关系了
下面是6个小时思考加独立书写的代码,步骤划分十分清楚
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<vector> #define il inline using namespace std; const int maxn=1e4+11; const int maxm=1e5+11; inline int read() { register int x=0,f=1; register char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); } while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); return x*f; } ///////////////////////////////// //题意数据 int n,m,val[maxn]; ///////////////////////////////// //链式前向星建边 struct edge{ int from,to,nxt; }e[maxm]; int h[maxn],Qxxcnt; il void addedge(int x,int y) { Qxxcnt++; e[Qxxcnt].from=x,e[Qxxcnt].to=y,e[Qxxcnt].nxt=h[x]; h[x]=Qxxcnt; } /////////////////////////////////////////// //Tarjan int timer,STACKcnt,SCCcnt; int dfn[maxn],_low[maxn],_stack[maxn]; bool vis[maxn]; int color[maxn];//每个点所属的强连通编号 int SCCval[maxn]; il void Tarjan(int x) { dfn[x]=_low[x]=++timer; ++STACKcnt; _stack[STACKcnt]=x; vis[x]=1; for(int i=h[x]; i ;i=e[i].nxt) { int v=e[i].to; if(!dfn[v]) { Tarjan(v); _low[x]=min(_low[x],_low[v]); } else if(vis[v]) { _low[x]=min(_low[x],dfn[v]); } } if(dfn[x]==_low[x]) { SCCcnt++; while(1) { color[_stack[STACKcnt]]=SCCcnt;//记录该点的SCC SCCval[SCCcnt]+=val[_stack[STACKcnt]] ;//将该点的值贡献给SCC vis[_stack[STACKcnt]]=0; STACKcnt--;//出栈 if(x==_stack[STACKcnt+1]) break;//当x刚好被请出去的时候 } } } /////////////////////////////////////////////////// //拓扑排序 vector<int > G[maxn]; vector<int > rdr[maxn];//已知终点,记录一条边的起点 queue<int > q; int in[maxn];//入度 int ans[maxn];//拓扑顺序 int TOPOcnt; void build_init()//建立拓扑排序初始化 { for(int i=1;i<=Qxxcnt;i++)//按边读取关系 { int x=color[e[i].from], y=color[e[i].to];//该边两点所属的SCC都求出来,判断关系建边 if(x!=y) { G[x].push_back(y); in[y]++; rdr[y].push_back(x); } } } void topo() { for(int i=1;i<=SCCcnt;i++) { if(in[i]==0) q.push(i); } while(!q.empty()) { int u=q.front(); ans[++TOPOcnt]=u; q.pop(); for(int i=0;i<G[u].size(); i++) { int v=G[u][i]; in[v]--; if(in[v]==0) q.push(v); } } } /////////////////////////////////////////////////////////////////////////// //动态规划 int f[maxn]; void dp_work() { for(int i=1;i<=SCCcnt;i++) { int w=ans[i];//拓扑顺序,w为当前SCC编号 f[w]=SCCval[w]; for(int j=0;j<rdr[w].size();++j) { int fa=rdr[w][j]; f[w]=max(f[w],f[fa]+SCCval[w]); } } int final_ans=-1; for(int i=1;i<=SCCcnt;i++) { final_ans=max(final_ans,f[i]); } printf("%d\n",final_ans); } /////////////////////////////// //主函数 int main() { //freopen("data.in","r",stdin); n=read(),m=read(); for(int i=1;i<=n;i++) val[i]=read(); for(int i=1;i<=m;i++) { int x,y; x=read(),y=read(); addedge(x,y); } /////////// for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i); /////////// build_init(); topo(); /////////// dp_work(); return 0; }```