洛谷P3387 縮點模板(縮點+記憶化搜尋)
阿新 • • 發佈:2018-10-31
題目連結:https://www.luogu.org/problemnew/show/P3387
如果你還不會Tarjan縮點,我見一你還是先看看這篇部落格:https://www.cnblogs.com/WWHHTT/p/9825766.html
或者過一段時間再來
首先我們分析題目,要求出圖中的一條路徑上點權之和最大的路徑的點權之和
那麼我們首先可以明確塗上如果有一個環,就一定要走完
或者說有強連通分量的話就要走完,我們便自然而然的想到了割點
最後處理的話就是一個簡單的記憶化搜尋
下面給出程式碼:
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> using namespace std; inline int min(int a,int b){return a<b?a:b;} inline int max(int a,int b){return a>b?a:b;} inline int rd(){ int x=0,f=1; char ch=getchar();for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void write(int x){ if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10+'0'); return ; } int n,m; int v[100006]; int head1[100006],nxt1[200006],to1[200006]; int total1=0; void add1(int x,int y){ total1++; to1[total1]=y; nxt1[total1]=head1[x]; head1[x]=total1; return ; } int dfn[100006],low[100006]; int color[100006]; int tot=0; int color_cnt=0; int book[100006]; int sta[100006]; int v2[100006]; int size=0; void Tarjan(int x,int fa){//Tarjan模板 low[x]=dfn[x]=++tot; sta[++size]=x; book[x]=1; for(int e=head1[x];e;e=nxt1[e]){ if(!dfn[to1[e]]){ Tarjan(to1[e],x); low[x]=min(low[x],low[to1[e]]); } else if(book[to1[e]]) low[x]=min(low[x],dfn[to1[e]]); } if(dfn[x]==low[x]){ color[x]=++color_cnt; v2[color_cnt]=v[x]; book[x]=0; while(sta[size]!=x){ color[sta[size]]=color_cnt; book[sta[size]]=0; v2[color_cnt]+=v[sta[size]];//這個點的權值的計算,根據題目來確定 size--; } size--; } return ; } int head2[100006],nxt2[200006],to2[200006]; int total2=0; void add2(int x,int y){ total2++; to2[total2]=y; nxt2[total2]=head2[x]; head2[x]=total2; return ; } void change(){ for(int i=1;i<=n;i++){ for(int e=head1[i];e;e=nxt1[e]){ if(color[i]!=color[to1[e]]){ add2(color[i],color[to1[e]]);//將兩個點連邊 } } } return ; } int dis[100006]; void dfs(int x){ if(dis[x]) return ; int sum=0; dis[x]=v2[x]; for(int e=head2[x];e;e=nxt2[e]){ if(!dis[to2[e]]) dfs(to2[e]); sum=max(sum,dis[to2[e]]); } dis[x]+=sum; return ; } int main(){ n=rd(),m=rd(); for(int i=1;i<=n;i++) v[i]=rd();//得到點權 for(int i=1;i<=m;i++){ int x=rd(),y=rd(); add1(x,y);//有向邊 } for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i,0); change(); int ans=0; for(int i=1;i<=color_cnt;i++){ if(!dis[i]){ dfs(i); ans=max(ans,dis[i]); } } write(ans); return 0; }