图论:P3387【模板】缩点 tarjan
P3387【模板】缩点
题目:
题目思路:
把一个有环图转换成无环图,就是利用tarjan算法,求出强连通分量,利用一个标记数组,将第n组强联通分量都标记为n,然后清空邻接矩阵,利用标记数组重新建立邻接关系,就是遍历每一个邻接关系,如果这两个点同属一个联通块就跳过,否则成为邻接关系。注意把点权压缩进缩的点,用一个二重循环,外层循环强联通分量的种数tot,内层遍历所有点,如果这个点被染成了外层循环i的数字i,sum[i]+=此点的点权,这样就缩边并且缩权值为一点了,或者在tarjan出栈的时候直接求和也可以,更简单。然后就求最大路径,利用DFS 树形DP求解。
一、缩点:
①tarjan:
void tarjan(int u) { dfn[u]=low[u]=++num; s.push(u); for(int i=0;i<linjie[u].size();++i) { int v=linjie[u][i]; if(!dfn[v]) { tarjan(v); low[u]=min(low[v],low[u]); } else if(!vis[v])//v已经入栈 说明 u v已经构成环 { low[u]=low[v]; } } if(dfn[u]==low[u]) { tot++; while(s.top()!=u) { liantong[s.top()]=tot;//把同属一联通块的染成一种颜色 vis[s.top()]=1; s.pop(); } liantong[u]=tot; vis[s.top()]=1; s.pop(); } }
②重新构建邻接关系:
1 memset(linjie,0,sizeof(linjie));//清空 重新构建邻接关系 2 for(int i=1;i<=m;++i) 3 { 4 if(liantong[a[i]]!=liantong[b[i]]) 5 { 6 linjie[liantong[a[i]]].push_back(liantong[b[i]]); 7 } 8 }
③:把联通块的权值求和
for(int i=1;i<=tot;++i) { for(int j=1;j<=n;++j) { if(liantong[j]==i) { sum[i]+=val[j]; } } }
最后完整代码:
#include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<stack> #include<queue> using namespace std; const int maxn = 1e5 + 5; const int maxm = 1e5 + 5; stack<int>s; int dfn[maxn], low[maxn]; vector<int>linjie[maxn]; int a[maxn], b[maxn], val[maxn];//起点和终点数组 权值数组 int liantong[maxn]; bool vis[maxn]; int num, tot; int n, m; int f[maxn];//存储以i为起点搜索图的最大路径和 int sum[maxn];//起来联通块和 void tarjan(int u) { dfn[u] = low[u] = ++num; s.push(u); for (int i = 0; i < linjie[u].size(); ++i) { int v = linjie[u][i]; if (!dfn[v]) { tarjan(v); low[u] = min(low[v], low[u]); } else if (!vis[v])//v已经入栈 说明 u v已经构成环 { low[u] = min(dfn[v],low[u]); } } if (dfn[u] == low[u]) { tot++; while (s.top() != u) { liantong[s.top()] = tot; vis[s.top()] = 1; sum[tot] += val[s.top()]; s.pop(); } liantong[u] = tot; sum[tot] += val[s.top()];//直接算出联通块的权值和 vis[s.top()] = 1; s.pop(); } } void dfs(int x) { if (f[x])return;//松弛过的点不需要再松弛 f[x] = sum[x]; int maxsum = 0; for (int i = 0; i < linjie[x].size(); ++i) { int v = linjie[x][i]; if (!f[v]) dfs(v); maxsum = max(maxsum, f[v]); } f[x] += maxsum; } int main() { ios::sync_with_stdio(false); cin >> n >> m; for (int i = 1; i <= n; ++i) { cin >> val[i]; } for (int j = 1; j <= m; ++j) { cin >> a[j] >> b[j]; linjie[a[j]].push_back(b[j]); } for (int i = 1; i <= n; ++i) { if (!dfn[i]) { tarjan(i); } } memset(linjie, 0, sizeof(linjie)); for (int i = 1; i <= m; ++i) { if (liantong[a[i]] != liantong[b[i]]) { linjie[liantong[a[i]]].push_back(liantong[b[i]]); } } int ans = 0; for (int i = 1; i <= tot; ++i) { if (!f[i]) { dfs(i); ans = max(ans, f[i]); } } cout << ans; return 0; }