Tarjan算法的一个重要应用就是缩点。
放上题目
题目背景
缩点+DP
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
Input
2 2 1 1 1 2 2 1
Output
2
说明
n<=10^4,m<=10^5,点权<=1000
算法:Tarjan缩点+DAGdp
这道题,在我看来,其实与Tarjan并无区别。
缩点,算法就是名字啊。其意在于将一个环(强连通分量)当做一个点来处理。
所以我们采用Tarjan求出所有的强连通分量,然后建出DAG(缩点)。
最后再在建出的新图上面进行DP记搜,找到点权最大的就好了啊。
放上代码。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 using namespace std; 8 9 const int Ma=1e4+5; 10 int n,m,val[Ma]; 11 int head[Ma],ecnt; 12 int dfn[Ma],low[Ma],ti; 13 int sk[Ma],p; 14 bool vis[Ma]; 15 int in[Ma],dis[Ma]; 16 struct ss{ 17 int nxt,to; 18 int self; 19 }d[Ma*10]; 20 int fa[Ma]; 21 queue<int> q; 22 23 void add(int a,int b) { 24 d[++ecnt].to=b; 25 d[ecnt].self=a; 26 d[ecnt].nxt=head[a]; 27 head[a]=ecnt; 28 } 29 30 void Tar(int now) { 31 dfn[now]=low[now]=++ti; 32 sk[++p]=now; 33 vis[now]=1; 34 for(int i=head[now];i;i=d[i].nxt) { 35 int nx=d[i].to; 36 if(!dfn[nx]) { 37 Tar(nx); 38 low[now]=min(low[now],low[nx]); 39 } 40 else if(vis[nx]) low[now]=min(low[now],dfn[nx]); 41 } 42 if(low[now]==dfn[now]) { 43 while(sk[p+1]!=now) { 44 val[now]+=val[sk[p]]; 45 fa[sk[p]]=now; 46 vis[sk[p]]=0; 47 --p; 48 } 49 val[now]>>=1; 50 } 51 } 52 53 void po() { 54 for(int i=1;i<=n;i++) 55 if(fa[i]==i) { 56 dis[i]=val[i]; 57 if(!in[i]) q.push(i); 58 } 59 while(!q.empty()) { 60 int now=q.front(); 61 for (int i=head[now];i;i=d[i].nxt) { 62 int nxt=d[i].to; 63 dis[nxt]=max(dis[nxt],dis[now]+val[nxt]); 64 in[nxt]--; 65 if(!in[nxt]) q.push(nxt); 66 } 67 q.pop(); 68 } 69 return; 70 } 71 72 int main() 73 { 74 scanf("%d%d",&n,&m); 75 for(int i=1;i<=n;i++) scanf("%d",&val[i]); 76 for(int i=1;i<=m;i++) { 77 int a,b; 78 scanf("%d%d",&a,&b); 79 add(a,b); 80 } 81 for(int i=1;i<=n;i++) 82 if(!dfn[i]) Tar(i); 83 ecnt=0; 84 memset(head,0,sizeof head); 85 for(int i=1;i<=m;i++) { 86 int x=fa[d[i].self]; 87 int y=fa[d[i].to]; 88 if(x!=y) { 89 add(x,y); 90 in[y]++; 91 } 92 } 93 po(); 94 int ans=0; 95 for(int i=1;i<=n;i++) 96 if(fa[i]==i) ans=max(ans,dis[i]); 97 cout<<ans<<endl; 98 return 0; 99 }
完成!可以静候AC了。
你们这题可以用个快读,稍微优化优化,然后就.......
祝AC愉快!