1. 程式人生 > >hihoCoder#1185 : 連通性·三 tarjan求強聯通分量 縮點 dfs/拓撲排序求路徑和最大值

hihoCoder#1185 : 連通性·三 tarjan求強聯通分量 縮點 dfs/拓撲排序求路徑和最大值

連通 namespace 關系 ont name problems lan 能夠 blog

題目鏈接:

http://hihocoder.com/problemset/problem/1185#

題意:

n個點,每個點有一個權值,m條有向邊,從1出發,每走到一個點, 就吃掉這個點的草,當沒有可以到達的草場或是能夠到達的草場都已經被吃光了之後就要返回到1了。求最多可以吃掉多少草。

思路:

提示裏面講的挺好的

如果草場是一個強連通圖,那麽我們只要走到任意一點,就可以把其他所有的草場都走一遍,並且可以選擇任意一個點作為終點。所以把強聯通塊縮成一個點

因為一個強連通塊會被縮成一個點,那麽我們可以直接建立一個新的圖,這個圖中的點對應的是每一個強連通塊。若原圖中存在邊(u,v),連接了屬於強連通分量A的點u和屬於強連通分量B的點v,那麽我們就在新圖中建立一條邊(A,B)。

先根據原圖找到所有的強連通分量,然後再根據原圖邊的關系建立新的圖,之後再用拓撲排序來處理就可以得到最終結果。

縮點之後就沒有環了,也就是說我們要求的答案在某條鏈上面,可以直接dfs下去,找到最大值,也可以用拓撲排序

拓撲排序就是每次取入度為0的點,用它去更新它所能到達的點的最大值,我們不能直接用點的權值更新這個點的答案,因為當有多條邊指向一個點的時候,可能會有重復的權值加進來,比如一個點通過兩條不同的路徑到達另一個點,a->b,b->c, a->d,d->c, 那麽a點的權值會在c點加兩次,所以我們需要新開一個數組表示答案,本來的權值要保留。

代碼:

dfs:

 1 #include <bits/stdc++.h>
 2
using namespace std; 3 typedef long long ll; 4 #define MS(a) memset(a,0,sizeof(a)) 5 #define MP make_pair 6 #define PB push_back 7 const int INF = 0x3f3f3f3f; 8 const ll INFLL = 0x3f3f3f3f3f3f3f3fLL; 9 inline ll read(){ 10 ll x=0,f=1;char ch=getchar(); 11 while(ch<0||ch>9){if(ch==
-)f=-1;ch=getchar();} 12 while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();} 13 return x*f; 14 } 15 ////////////////////////////////////////////////////////////////////////// 16 const int maxn = 1e5+10; 17 18 int n,m,tot; 19 vector<int> g[maxn]; 20 int low[maxn],dfn[maxn],vis[maxn],ins[maxn],r[maxn],w[maxn]; 21 stack<int> s; 22 23 void tarjan(int u){ 24 vis[u] = ins[u] = 1; 25 dfn[u] = low[u] = ++tot; 26 s.push(u); 27 for(int i=0; i<(int)g[u].size(); i++){ 28 int v = g[u][i]; 29 if(!vis[v]){ 30 tarjan(v); 31 low[u] = min(low[u],low[v]); 32 }else if(ins[v]){ 33 low[u] = min(low[u],dfn[v]); 34 } 35 } 36 int sum = 0; 37 if(dfn[u] == low[u]){ 38 int t; 39 do{ 40 t = s.top(); 41 s.pop(); 42 ins[t] = 0; 43 sum += w[t]; 44 r[t] = u; 45 }while(u != t); 46 w[u] = sum; 47 } 48 } 49 50 void upd(){ 51 for(int i=1; i<=n; i++){ 52 if(i != r[i]){ 53 for(int j=0; j<(int)g[i].size(); j++){ 54 int v = g[i][j]; 55 if(v != r[i]) g[r[i]].push_back(v); 56 } 57 } 58 } 59 } 60 61 int ans,sum; 62 void dfs(int u){ 63 sum += w[u]; 64 for(int i=0; i<(int)g[u].size(); i++){ 65 int v = g[u][i]; 66 if(u == r[v]) continue; 67 dfs(v); 68 } 69 ans = max(ans,sum); 70 sum -= w[u]; 71 } 72 73 int main(){ 74 cin >> n >> m; 75 for(int i=1; i<=n; i++) 76 scanf("%d",&w[i]); 77 for(int i=0; i<m; i++){ 78 int u,v; scanf("%d%d",&u,&v); 79 g[u].push_back(v); 80 } 81 for(int i=1; i<=n; i++) 82 if(!dfn[i]) tarjan(1); 83 upd(); 84 dfs(1); 85 86 cout << ans << endl; 87 88 return 0; 89 }

拓撲排序:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 #define MS(a) memset(a,0,sizeof(a))
  5 #define MP make_pair
  6 #define PB push_back
  7 const int INF = 0x3f3f3f3f;
  8 const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
  9 inline ll read(){
 10     ll x=0,f=1;char ch=getchar();
 11     while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
 12     while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
 13     return x*f;
 14 }
 15 //////////////////////////////////////////////////////////////////////////
 16 const int maxn = 1e5+10;
 17 
 18 int n,m,tot;
 19 vector<int> g[maxn];
 20 int low[maxn],dfn[maxn],vis[maxn],ins[maxn],r[maxn],w[maxn],pp[maxn],in[maxn];
 21 
 22 void tarjan(int u){
 23     vis[u] = ins[u] = 1;
 24     dfn[u] = low[u] = ++tot;
 25     s.push(u);
 26     for(int i=0; i<(int)g[u].size(); i++){
 27         int v = g[u][i];
 28         if(!vis[v]){
 29             tarjan(v);
 30             low[u] = min(low[u],low[v]);
 31         }else if(ins[v]){
 32             low[u] = min(low[u],dfn[v]);
 33         }
 34     }
 35     int sum = 0;
 36     if(dfn[u] == low[u]){
 37         int t;
 38         do{
 39             t = s.top();
 40             s.pop();
 41             ins[t] = 0;
 42             sum += w[t];
 43             r[t] = u;
 44         }while(u != t);
 45         w[u] = sum;
 46     }
 47 }
 48 
 49 void upd(){
 50     for(int i=1; i<=n; i++){
 51         if(i != r[i]){
 52             for(int j=0; j<(int)g[i].size(); j++){
 53                 int v = g[i][j];
 54                 if(v != r[i]) g[r[i]].push_back(v);
 55             }
 56         }
 57     }
 58 }
 59 void cal(){
 60     for(int i=1; i<=n; i++){
 61         if(i != r[i]) continue;
 62         for(int j=0; j<(int)g[i].size(); j++){
 63             int v = g[i][j];
 64             if(r[v] == r[i]) continue;
 65             // cout << v << " " << r[v] << endl;
 66             in[r[v]]++;
 67         }
 68     }
 69 }
 70 
 71 int ans;
 72 
 73 int main(){
 74     cin >> n >> m;
 75     for(int i=1; i<=n; i++)
 76         scanf("%d",&w[i]);
 77     for(int i=0; i<m; i++){
 78         int u,v; scanf("%d%d",&u,&v);
 79         g[u].push_back(v);
 80     }
 81     for(int i=1; i<=n; i++)
 82         if(!dfn[i]) tarjan(1);
 83     upd(); cal();
 84     // for(int i=1; i<=n; i++)
 85     //     cout << in[i] << endl;
 86     queue<int> q;
 87     q.push(r[1]);
 88     pp[1] = w[r[1]];
 89     while(!q.empty()){
 90         int u = q.front(); q.pop();
 91         if(u != r[u]) continue;
 92         ans = max(ans,pp[u]);
 93         for(int i=0; i<(int)g[u].size(); i++){
 94             int v = g[u][i];
 95             if(u == r[v]) continue;
 96             in[v]--;
 97             pp[v] = max(pp[v],w[v]+pp[u]);
 98             if(in[v] == 0) q.push(v);
 99         }
100     }
101 
102     cout << ans << endl;
103 
104     return 0;
105 }

hihoCoder#1185 : 連通性·三 tarjan求強聯通分量 縮點 dfs/拓撲排序求路徑和最大值