http://2014.sprout.csie.org/oj/pro/164/
這題是DAG(有向無環圖)上的DP回朔。
題目要求所有路徑中最大的路徑上總共有幾個點。因為路徑可能一樣大,回朔會變成一張圖。
首先建圖,之後把入度是0的點丟到queue裡,以類似SPFA的方式鬆弛點連出去的點,把DP值更新,因為要回朔,假設A連到B,如果A點的DP值加上B點的權重會大於B點的DP值,就把B點的回朔邊清空,加上一條指向A點的邊。如果A點的DP值加上B點的權重會等於B點的DP值,則增加一條回朔邊指向A(不清空)。
做完順便將B點的入度減1,當入度為0時將該點丟到queue裡(已經沒有點會更新它了)。
如果一個點的出度為0,我就將他連到特殊點,方法跟上面一樣。
最後只要對特殊點的回朔圖(也是一張DAG)做DFS,計算點的數量,就可以得到答案了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #include <cstdlib> #include <iostream> #include <cstring> #include <vector> #include <queue> #define N 100001 #define F(n) Fi(i,n) #define Fi(i,n) Fl(i,0,n) #define Fl(i,l,n) for(int i=l;i<n;i++) using namespace std; vector<int>D[N],B[N]; int T[N],TDP[N],IN[N]; bool wed[N]; int DFS(int now){ wed[now]=true; int sum=1; F(B[now].size())if(!wed[B[now][i]])sum+=DFS(B[now][i]); return sum; } int main(int argc,char *argv[]) { ios_base::sync_with_stdio(false); int n,m,a,b; cin>>n>>m; F(n)cin>>TDP[i+1]; F(m){ cin>>a>>b; D[a].push_back(b); T[b]=TDP[b]; IN[b]++; } queue<int>qq; F(n)if(IN[i+1]==0)qq.push(i+1); while(!qq.empty()){ int now=qq.front(); qq.pop(); if(D[now].size()==0){ if(TDP[now]>TDP[0]){ TDP[0]=TDP[now]; B[0].clear(); B[0].push_back(now); }else if(TDP[now]==TDP[0]){ B[0].push_back(now); } continue; } F(D[now].size()){ if(TDP[now]+T[D[now][i]]>TDP[D[now][i]]){ TDP[D[now][i]]=TDP[now]+T[D[now][i]]; B[D[now][i]].clear(); B[D[now][i]].push_back(now); }else if(TDP[now]+T[D[now][i]]==TDP[D[now][i]]){ B[D[now][i]].push_back(now); } if(--IN[D[now][i]]==0){ qq.push(D[now][i]); } } } cout<<DFS(0)-1<<endl; return 0; }
|