• 强联通分量


    2020 10 .31

    补记一下强连通分量的笔记

    内容来自OI-wiki和yu__xuan 的讲课

    强联通分量(个人理解):

    • 在一个无向图中,u 能到v ,v 也能到u

    • 在有向图中u能到v,v也能到u那么很显然 , u---v可以构成一个环

    • 强联通分量就是把这个点集缩成一个点,于是乎,这个图变成了一个DAG(有向无环图) 然后你就可在这个新图上根据DAG的性质开始做题,比如 拓扑

    这里只记录自己最熟悉的tarjan算法

    tarjan 算法是由栈来实现的

    变量声明:

    dfn[u] ---->dfs序
    low[u] ---->以u为根的子树,最小的dfs序
    

    那么会出现 3 中情况:

    • v 未被访问:继续对 v 进行深度搜索。在回溯过程中,用 low[v] 更新 low[u]。因为存在从 u 到 v 的直接路径,所以 v 能够回溯到的已经在栈中的结点,u 也一定能够回溯到。

    • v 被访问过,还在栈中:即已经被访问过,根据low的定义(能够回溯到的最早的已经在栈中的结点),则用dfn[v]更新low[u]。

    • v 被访问过,已不在栈中:说明 v 所在的强连通分量已经找出,不可能和 u 在一个强连通分量中,所以不用操作。

      by---yu__xuan学姐

    targan:

    void tarjan(int u){
    	st[++sn]=u,vis[u]=1;
    	dfn[u]=low[u]=++cnt;
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(!dfn[v]){
    			tarjan(v),low[u]=min(low[u],low[v]);
    		}
    		else if(vis[v]==1) low[u]=min(low[u],dfn[v]);
    	}
    	if(low[u]==dfn[u]){
    		int top=st[sn--];vis[top]=0;
    		scc++; siz[scc]++,num[top] = scc;	 
    		while(top!=u){
    			top=st[sn--];
    			vis[top]=0;
    			num[top] = scc,siz[scc]++;
    		}
    	}
    }//变量注释在下一个板子里面 
    

    另一种短一点的板子

    void tarjan(int u){
    	dfn[u] = low[u] = ++cnt;
    	st[++sn] = u,vis[u] = 1;
    	for(int i = head[u] ; i ; i = e[i].next){
    		int to = e[i].to;
    		if(!dfn[to]) low[u] = min(low[u],low[to]);
    		else if(vis[to]) low[u] = min(low[u], dfn[to]);
    		//if(vis[to]) low[u] = min(low[u], low[to]);
    		//这两种写法都对 
                    //但是对于割点(顶)来说就只有第一种写法对了,可以看我割点的博客
    	}
    	if(low[u] == dfn[u]){
    		int top ,scc++;//scc--第几个强联通分量 
    		do{
    			top = st[sn--]; vis[top] = 0;
    			num[top] = scc; siz[scc]++;
    			//top所对应的第几个强联通分量,这个强联通分量里面有几个点 
    			//上一个板子可以跟着这个修改一下 
    			 
    		}while(top != u); 
    	} 
    }
    

    实现code(以前写的有点丑):

    模板

    
    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int M=5e4+10;
    const int N=1e4+10;
    int n,m,nume,head[N];
    int st[M],sn,cnt,dfn[N],low[N];
    bool vis[N];
    struct node{
    	int to,next;
    }e[M<<1];
    void add_edge(int from,int to){
    	e[++nume].next = head[from];
    	e[nume].to=to;
    	head[from]=nume;
    }
    bool falg=0;
    int ans;
    void tarjan(int u){
    	st[++sn]=u,vis[u]=1;
    	dfn[u]=low[u]=++cnt;
    	for(int i=head[u];i;i=e[i].next){
    		int v=e[i].to;
    		if(dfn[v]==0){
    			tarjan(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else 
    		if(vis[v]==1)
    		low[u]=min(low[u],dfn[v]);
    	}
    	if(low[u]==dfn[u]){
    		int top=st[sn--];
    		vis[top]=0;
    		while(top!=u){
    			top=st[sn--];
    			vis[top]=0;
    			falg=1;
    		}
    		
    	}
    	if(falg==1) 
    	falg=0,ans++;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,u,v;i<=m;i++){
    		scanf("%d%d",&u,&v);
    		add_edge(u,v);
    		//add_edge(v,u);
    	}for(int i=1;i<=n;i++){
    		if(dfn[i]!=0) continue;
    		tarjan(i);
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    update on 2021.1.31

    修一下以前写的博客,以前写的真是惨不忍睹现在也是加两道有意思的题

    间谍网络

    solution:

    • 可以很显然的发现如果一个罪犯不能被收买并且没有人能揭发他,那必然是无解的
    • 如果有人能揭发他或者能收买他,那就简单的跑缩点,然后统计入度为零的点的收买代价(缩点时记录整个点的信息)
    • 考虑无解时如何输出最小点号,可以让所有能收买或者揭发的人跑一遍缩点,从小到大枚举点号,如果这个人既不能被揭发也不能被收买,直接输出,(return~~0)即可

    code:

    
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <string>
    #include <queue>
    #define ll long long
    using namespace std;
    const int N = 1e4 + 100;
    const int inf = 1e9;
    int read() {
    int s = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch))
      f |= (ch == '-'), ch = getchar();
    while (isdigit(ch))
      s = s * 10 + (ch ^ 48), ch = getchar();
    return f ? -s : s;
    }
    struct Edge {
    int from, to, net;
    } e[N * N] ;
    int head[N], nume;
    void add_edge(int from, int to) {
    e[++nume] = (Edge){from, to, head[from]};
    head[from] = nume;
    }
    int st[N], sn, vis[N];
    int dfn[N], low[N], cnt;
    int scc,rd[N], cd[N],minn[N];
    int cost[N];
    int num[N];
    void tir(int x) {
    st[++sn] = x, vis[x] = 1;
    low[x] = dfn[x] = ++cnt;
    for (int i = head[x]; i; i = e[i].net) {
      int to = e[i].to;
      if (!dfn[to])
        tir(to), low[x] = min(low[x], low[to]);
      else if (vis[to])
        low[x] = min(low[x], dfn[to]);
    }
    if (dfn[x] == low[x]) {
      int top = st[sn--];
      vis[top] = 0;scc++;
      minn[++scc] = inf;
      minn[scc] = min(minn[scc],cost[top]);
      num[top] = scc;
      while (top != x) {
        top = st[sn--];
        vis[top] = 0;
        minn[scc] = min(minn[scc],cost[top]);
        num[top] = scc;
      }
    }
    }
    int main() {
    int n = read() , p = read();
    for(int i = 1 ; i<= n; i++) cost[i] = inf;
    for (int i = 1; i <= p; i++) {
      int x = read();
      cost[x] = read();
    }
    int r = read();
    for(int i = 1 ; i <= r ; i++) {
      int u = read() , v = read();
      add_edge(u ,v);
    }
    for (int i = 1; i <= n; i++) {
      if (!dfn[i] && cost[i] != inf) 
        tir(i);
    }
    for(int i = 1 ; i<= n ;i++) {
      if(!dfn[i]) {
        puts("NO");
        printf("%d",i);
        system("pause");
        return 0;
      }
    }
    for(int i = 1 ; i <= nume ; i++) {
      if(num[e[i].from] == num[e[i].to]) continue;
      cd[num[e[i].from]]++;
      rd[num[e[i].to]]++;
    }
    int ans = 0;
    for(int i = 1 ; i <= scc ;i++) {
      if(rd[i] == 0) ans += minn[i];
    }
    puts("YES");
    printf("%d",ans);
    system("pause");
    return 0;
    }
    

    抢夺计划

    solution:

    • 首先跑一遍裸的缩点,同时将每个点的点权记录到每个点中,然后用spfa跑一遍最长路,如果到达的点有标记(是酒馆),那就取一次( ext{max})
      code:
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <string>
    #define ll long long
    using namespace std;
    const int N = 5e5 + 100;
    const int inf = 1e9;
    int read() {
      int s = 0, f = 0;
      char ch = getchar();
      while (!isdigit(ch))
        f |= (ch == '-'), ch = getchar();
      while (isdigit(ch))
        s = s * 10 + (ch ^ 48), ch = getchar();
      return f ? -s : s;
    }
    struct Edge {
      int from, to, net;
    } e[N], G[N];
    int head[N], nume;
    void add_edge(int from, int to) {
      e[++nume] = (Edge){from, to, head[from]};
      head[from] = nume;
    }
    int headg[N], numg;
    void another(int from, int to) {
      G[++numg] = (Edge){from, to, headg[from]};
      headg[from] = numg;
    }
    int st[N], sn, vis[N];
    int dfn[N], low[N], cnt;
    int scc, rd[N], cd[N], sum[N];
    int val[N], flag[N], FLAG[N];
    int num[N];
    int n, m, s, p;
    int start;
    void tir(int x) {
      st[++sn] = x, vis[x] = 1;
      low[x] = dfn[x] = ++cnt;
      for (int i = head[x]; i; i = e[i].net) {
        int to = e[i].to;
        if (!dfn[to])
          tir(to), low[x] = min(low[x], low[to]);
        else if (vis[to])
          low[x] = min(low[x], dfn[to]);
      }
      if (dfn[x] == low[x]) {
        int top = st[sn--];
        vis[top] = 0;
        scc++;
        if (top == s)
          start = scc;
          FLAG[scc] += flag[top];
        sum[scc] += val[top];
        num[top] = scc;
        while (top != x) {
          top = st[sn--];
          if (top == s)
            start = scc;
          vis[top] = 0;
          sum[scc] += val[top];
          num[top] = scc;
          FLAG[scc] += flag[top];
        }
      }
    }
    int ans, dis[N];
    bool VIS[N];
    void spfa() {
      queue<int> q;
      q.push(start);
      VIS[start] = 1;
      dis[start] = sum[start];
      while (!q.empty()) {
        int fr = q.front();
        q.pop();
        VIS[fr] = 0;
        for (int i = headg[fr]; i; i = G[i].net) {
          int to = G[i].to;
          if (dis[to] < dis[fr] + sum[to]) {
            dis[to] = dis[fr] + sum[to];
            if (!VIS[to]) {
              q.push(to), VIS[to] = 1;
            }
          }
        }
      }
    }
    int main() {
      n = read(), m = read();
      for (int i = 1; i <= m; i++) {
        int u = read(), v = read();
        add_edge(u, v);
      }
      for (int i = 1; i <= n; i++)
        val[i] = read();
      s = read(), p = read();
      for (int i = 1; i <= n; i++) {
        if (!dfn[i])
          tir(i);
      }
      for (int i = 1; i <= p; i++) {
        int x = read();
        flag[x] = 1;
      }
      for (int i = 1; i <= m; i++) {
        if (num[e[i].from] == num[e[i].to])
          continue;
        another(num[e[i].from], num[e[i].to]);
        rd[num[e[i].to]]++;
      }
      spfa();
      for (int i = 1; i <= n; i++) {
        if(flag[i])
        ans = max(ans, dis[num[i]]);
        // cout << dis[i] << " ";
      }
      printf("%d", ans);
      system("pause");
      return 0;
    }
    
    Future never has to do with past time,but present time dose.
  • 相关阅读:
    linux 和unix 的区别
    Ubuntu 12.04下安装ibus中文输入法
    安装vmware tools失败解决方法
    snort简介以及在Ubuntu下的安装
    ubuntu下tcpdump使用
    securecrt在linux与windows之间传输文件(转)
    大数据处理时用到maven的repository
    Spark之命令
    Spark之集群搭建
    Spark之scala
  • 原文地址:https://www.cnblogs.com/-wzd233/p/13911742.html
Copyright © 2020-2023  润新知