• 算法:强连通分量(SCC) Tarjan算法


    强连通分量,不能再加任何一个点了,再加一个点就不是强连通了

     

    1. vector<int>e[N];
    2. int dfn[N],low[N],tot;
    3. bool instk[N];
    4. int scc[N],siz[N],cnt;
    5. void tarjan(int x){
    6. //入x时,盖戳,入栈
    7. dfn[x]=low[x]=++tot;
    8. q.push(x);
    9. instk[x]=true;
    10. for(auto y:e[x]){
    11. if(!dfn[y]){//若y尚未访问
    12. tarjan(y);
    13. low[x]=min(low[x],low[y]);//回x时更新low
    14. }
    15. else if(instk[y]){//若y已被访问且在栈中
    16. low[x]=min(low[x],dfn[y]);//更新low
    17. }
    18. }
    19. //离x时,记录SCC
    20. if(dfn[x]==low[x]){//若x是SCC的根
    21. int y;
    22. cnt++;
    23. do{
    24. y=q.top();
    25. q.pop();
    26. instk[y]=false;
    27. scc[y]=cnt;//SCC编号
    28. siz[cnt]++;//SCC大小
    29. }while(y!=x);
    30. }
    31. }

    [USACO06JAN] The Cow Prom S - 洛谷

    AC代码:

    1. #include
    2. #define endl '\n'
    3. #define int long long
    4. using namespace std;
    5. const int N=1e4+10;
    6. int dfn[N],low[N],tot;
    7. bool instk[N];
    8. int siz[N],cnt;
    9. stack<int>q;
    10. vectorint>>e(N);
    11. int n,m;
    12. void tarjan(int x){
    13. dfn[x]=low[x]=++tot;
    14. q.push(x);
    15. instk[x]=true;
    16. for(auto v:e[x]){
    17. if(!dfn[v]){
    18. tarjan(v);
    19. low[x]=min(low[x],low[v]);
    20. }
    21. else if(instk[v]){
    22. low[x]=min(low[x],dfn[v]);
    23. }
    24. }
    25. if(dfn[x]==low[x]){
    26. int y;
    27. cnt++;
    28. do{
    29. y=q.top();
    30. q.pop();
    31. instk[y]=false;
    32. siz[cnt]++;
    33. }while(y!=x);
    34. }
    35. }
    36. void solve() {
    37. cin>>n>>m;
    38. while(m--){
    39. int a,b;
    40. cin>>a>>b;
    41. e[a].push_back(b);
    42. }
    43. for(int i=1;i<=n;i++){
    44. if(!dfn[i]) tarjan(i);
    45. }
    46. int ans=0;
    47. for(int i=1;i<=cnt;i++){
    48. if(siz[i]>1) ans++;
    49. }
    50. cout<
    51. }
    52. signed main() {
    53. ios::sync_with_stdio(false);
    54. cin.tie(0);
    55. cout.tie(0);
    56. int t=1;
    57. // cin>>t;
    58. while(t--) {
    59. solve();
    60. }
    61. return 0;
    62. }

     Trajan SCC缩点

     

    我们加边的时候让出度为0的点指向入度为0的点,那么只要max(din,dout)即可

    AC代码:

    1. #include
    2. #define endl '\n'
    3. #define int long long
    4. using namespace std;
    5. const int N=1e4+10;
    6. int dfn[N],low[N],tot;
    7. bool instk[N];
    8. int scc[N],cnt;
    9. int din[N],dout[N];//SCC的入度,出度
    10. int n;
    11. vectorint>>e(N);
    12. stack<int>q;
    13. void tarjan(int x){
    14. dfn[x]=low[x]=++tot;
    15. q.push(x);
    16. instk[x]=true;
    17. for(auto v:e[x]){
    18. if(!dfn[v]){
    19. tarjan(v);
    20. low[x]=min(low[x],low[v]);
    21. }
    22. else if(instk[v]){
    23. low[x]=min(low[x],dfn[v]);
    24. }
    25. }
    26. if(dfn[x]==low[x]){
    27. int y;
    28. cnt++;
    29. do{
    30. y=q.top();
    31. q.pop();
    32. instk[y]=false;
    33. scc[y]=cnt;
    34. }while(y!=x);
    35. }
    36. }
    37. void solve() {
    38. cin>>n;
    39. for(int i=1,a;i<=n;i++){
    40. cin>>a;
    41. while(a){
    42. e[i].push_back(a);
    43. cin>>a;
    44. }
    45. }
    46. for(int i=1;i<=n;i++){
    47. if(!dfn[i]) tarjan(i);//一些点可能走不到,即图不连通
    48. }
    49. for(int x=1;x<=n;x++){//枚举n个点
    50. for(int y:e[x]){//枚举点x的邻边,x指向y
    51. if(scc[x]!=scc[y]){//如果x和y所在的强连通分量不一样,即不在同一个强连通分量之内
    52. din[scc[y]]++;//y所在的强连通分量的入度++
    53. dout[scc[x]]++;//x所在的强连通分量的出度++
    54. }
    55. }
    56. }
    57. int a=0,b=0;
    58. for(int i=1;i<=cnt;i++){
    59. if(!din[i]) a++;//a表示缩点后入度为0的个数
    60. if(!dout[i]) b++;//b表示缩点后出度为0的个数
    61. }
    62. cout<
    63. if(cnt==1) cout<<0<//特判,如果只有一个强连通分量,那么就不用加边了
    64. else cout<<max(a,b)<
    65. }
    66. signed main() {
    67. ios::sync_with_stdio(false);
    68. cin.tie(0);
    69. cout.tie(0);
    70. int t=1;
    71. // cin>>t;
    72. while(t--) {
    73. solve();
    74. }
    75. return 0;
    76. }

    [USACO03FALL / HAOI2006] 受欢迎的牛 G - 洛谷 

     AC代码:

    1. #include
    2. #define endl '\n'
    3. #define int long long
    4. using namespace std;
    5. const int N=1e4+10;
    6. int dfn[N],low[N],tot;
    7. bool instk[N];
    8. int scc[N],siz[N];
    9. int cnt;
    10. int dout[N];
    11. vectorint>>e(N);
    12. stack<int>q;
    13. int n,m;
    14. void tarjan(int x){
    15. dfn[x]=low[x]=++tot;
    16. q.push(x);
    17. instk[x]=true;
    18. for(auto v:e[x]){
    19. if(!dfn[v]){
    20. tarjan(v);
    21. low[x]=min(low[x],low[v]);
    22. }
    23. else if(instk[v]){
    24. low[x]=min(low[x],dfn[v]);
    25. }
    26. }
    27. if(dfn[x]==low[x]){
    28. int y;
    29. cnt++;
    30. do{
    31. y=q.top();
    32. q.pop();
    33. instk[y]=false;
    34. scc[y]=cnt;
    35. siz[cnt]++;
    36. }while(y!=x);
    37. }
    38. }
    39. void solve() {
    40. cin>>n>>m;
    41. for(int i=0;i
    42. int a,b;
    43. cin>>a>>b;
    44. e[a].push_back(b);
    45. }
    46. for(int i=1;i<=n;i++){
    47. if(!dfn[i]) tarjan(i);
    48. }
    49. for(int x=1;x<=n;x++){
    50. for(auto y:e[x]){
    51. if(scc[x]!=scc[y]){
    52. dout[scc[x]]++;
    53. }
    54. }
    55. }
    56. int sum=0;
    57. int cnt1=0;
    58. for(int i=1;i<=cnt;i++){
    59. if(dout[i]==0){
    60. sum=siz[i];
    61. cnt1++;
    62. }
    63. }
    64. if(cnt1>1) sum=0;
    65. cout<
    66. }
    67. signed main() {
    68. ios::sync_with_stdio(false);
    69. cin.tie(0);
    70. cout.tie(0);
    71. int t=1;
    72. // cin>>t;
    73. while(t--) {
    74. solve();
    75. }
    76. return 0;
    77. }

    【模板】缩点 - 洛谷

    先缩点转化成一个无环图

     

    团号逆序是拓扑序,因为我们给强连通分量标号的时候是从1开始标的,于是团号小的在拓扑序的末端,这样从大到小枚举团号即为拓扑序,保证是线性的,这样dp的话才能满足无后效性

    AC代码:

    1. #include
    2. #define endl '\n'
    3. #define int long long
    4. using namespace std;
    5. const int N=1e4+10;
    6. int dfn[N],low[N],tot;
    7. bool instk[N];
    8. int scc[N],cnt;
    9. int w[N],nw[N];
    10. int n,m;
    11. vectorint>>e(N),ne(N);
    12. stack<int>q;
    13. int dp[N];
    14. void tarjan(int x){
    15. dfn[x]=low[x]=++tot;
    16. q.push(x);
    17. instk[x]=true;
    18. for(auto v:e[x]){
    19. if(!dfn[v]){
    20. tarjan(v);
    21. low[x]=min(low[x],low[v]);
    22. }
    23. else if(instk[v]){
    24. low[x]=min(low[x],dfn[v]);
    25. }
    26. }
    27. if(dfn[x]==low[x]){
    28. int y;
    29. cnt++;
    30. do{
    31. y=q.top();
    32. q.pop();
    33. instk[y]=false;
    34. scc[y]=cnt;
    35. }while(y!=x);
    36. }
    37. }
    38. void solve() {
    39. cin>>n>>m;
    40. for(int i=1;i<=n;i++) cin>>w[i];
    41. for(int i=0;i
    42. int a,b;
    43. cin>>a>>b;
    44. e[a].push_back(b);
    45. }
    46. for(int i=1;i<=n;i++){
    47. if(!dfn[i]) tarjan(i);
    48. }
    49. for(int x=1;x<=n;x++){
    50. nw[scc[x]]+=w[x];//新点的权值
    51. for(int y:e[x]){
    52. if(scc[x]!=scc[y]){
    53. ne[scc[x]].push_back(scc[y]);
    54. }
    55. }
    56. }//缩点后建拓扑图
    57. for(int x=cnt;x>=1;x--){
    58. if(dp[x]==0){//若x为路的起点
    59. dp[x]=nw[x];
    60. }
    61. for(auto y:ne[x]){
    62. dp[y]=max(dp[y],dp[x]+nw[y]);
    63. }
    64. }
    65. int ans=0;
    66. for(int i=1;i<=cnt;i++) ans=max(ans,dp[i]);//可能图不连通,有多个强连通分量
    67. cout<
    68. }
    69. signed main() {
    70. ios::sync_with_stdio(false);
    71. cin.tie(0);
    72. cout.tie(0);
    73. int t=1;
    74. // cin>>t;
    75. while(t--) {
    76. solve();
    77. }
    78. return 0;
    79. }
  • 相关阅读:
    早上空腹喝水,比不吃早餐更伤胃?起床后先做2件事
    唤起testFlight、唤起AppStore
    Django实现Hello World
    CocosCreator接入wwise的CocosAndroid.mk
    云服务器安装宝塔面板,如何对高并发大流量网站的优化方法
    拿下跨界C1轮投资,本土Tier 1高阶智能驾驶系统迅速“出圈”
    webpack配置自动压缩图片
    基本微信小程序的外卖点餐订餐平台
    Android进阶宝典 -- 手写RecyclerView分页组件
    ElasticSearch学习(四):的增删改查、高亮、聚合、别名、重建索引
  • 原文地址:https://blog.csdn.net/m0_74087709/article/details/133579545