強聯通份量

2020 10 .31node

補記一下強連通份量的筆記

內容來自OI-wiki和yu__xuan 的講課ios

強聯通份量(我的理解):c++

  • 在一個無向圖中,u 能到v ,v 也能到ugit

  • 在有向圖中u能到v,v也能到u那麼很顯然 , u---v能夠構成一個環算法

  • 強聯通份量就是把這個點集縮成一個點,因而乎,這個圖變成了一個DAG(有向無環圖) 而後你就可在這個新圖上根據DAG的性質開始作題,好比 拓撲網絡

這裏只記錄本身最熟悉的tarjan算法

tarjan 算法是由棧來實現的spa

變量聲明:

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

那麼會出現 3 中狀況:.net

  • v 未被訪問:繼續對 v 進行深度搜索。在回溯過程當中,用 low[v] 更新 low[u]。由於存在從 u 到 v 的直接路徑,因此 v 可以回溯到的已經在棧中的結點,u 也必定可以回溯到。code

  • v 被訪問過,還在棧中:即已經被訪問過,根據low的定義(可以回溯到的最先的已經在棧中的結點),則用dfn[v]更新low[u]。get

  • 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跑一遍最長路,若是到達的點有標記(是酒館),那就取一次\(\text{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;
}
相關文章
相關標籤/搜索