洛谷P3387 【模板】缩点

洛谷P3387 【模板】缩点

题目背景

缩点+DP

题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入输出格式

输入格式:

 

第一行,n,m

第二行,n个整数,依次代表点权

第三至m+2行,每行两个整数u,v,表示u->v有一条有向边

 

输出格式:

 

共一行,最大的点权之和。

 

输入输出样例

输入样例#1:
2 2
1 1
1 2
2 1
输出样例#1:
2

说明

n<=10^4,m<=10^5,|点权|<=1000 算法:Tarjan缩点+DAGdp

 

 

题解:模板缩点,马上就要NOIP了,当作复习一下。

缩点的思想是找到一个环,把这个环缩成一个点。若原图联通,则缩点并不影响环外的连通性。

因此对于这题,我们把环缩成点时,环内所有的点的权值和为新点的权值,然后从所有入度为零的点作为起点跑一边SPFA就能求得单向路径上的最大值。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<queue>
 5 #include<stack>
 6 using namespace std;
 7 const int N=10005,M=100005;
 8 int n,m,x,y,cnt,tot,ans,a[N],p[N],fro[M],to[M],nxt[M],head[N];
 9 int Cnt,To[M],Nxt[M],Head[M],deg[N],dfn[N],low[N],grp[N],dis[N];
10 bool vis[N],mark[N];
11 queue<int> Q;
12 stack<int> S;
13 void add(int u,int v){
14     fro[++cnt]=u; to[cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
15 }
16 void Add(int u,int v){
17     To[++Cnt]=v; Nxt[Cnt]=Head[u]; Head[u]=Cnt;
18 }
19 void Tarjan(int u)
20 {
21     vis[u]=mark[u]=1; S.push(u); dfn[u]=low[u]=++tot;
22     for (int i=head[u];i;i=nxt[i])
23     {
24         int v=to[i];
25         if (!dfn[v])
26         {
27             Tarjan(v);
28             low[u]=min(low[u],low[v]);
29         }
30         if (mark[v]) low[u]=min(low[u],dfn[v]);
31     }
32     if (low[u]==dfn[u])
33     {
34         ++tot;
35         int x=S.top();
36         while (x!=u)
37         {
38             grp[x]=tot;
39             p[tot]+=a[x];
40             mark[x]=0;
41             S.pop();
42             x=S.top();
43         }
44         grp[x]=tot;
45         p[tot]+=a[x];
46         mark[x]=0;
47         S.pop();
48     }
49 }
50 void SPFA(int x)
51 {
52     ans=max(ans,p[x]);
53     memset(vis,0,sizeof(vis));
54     memset(dis,0,sizeof(dis));
55     Q.push(x); dis[x]=p[x]; vis[x]=1;
56     while (!Q.empty())
57     {
58         int u=Q.front(); Q.pop(); vis[u]=0;
59         for (int i=Head[u];i;i=Nxt[i])
60         {
61             int v=To[i];
62             if (dis[u]+p[v]>dis[v])
63             {
64                 dis[v]=dis[u]+p[v];
65                 ans=max(ans,dis[v]);
66                 if (!vis[v]) vis[v]=1,Q.push(v);
67             }
68         }
69     }
70 }
71 int main()
72 {
73     scanf("%d%d",&n,&m);
74     for (int i=1;i<=n;++i) scanf("%d",&a[i]);
75     for (int i=1;i<=m;++i) scanf("%d%d",&x,&y),add(x,y);
76     for (int i=1;i<=n;++i) if (!vis[i]) Tarjan(i);
77     for (int i=1;i<=m;++i)
78     {
79         if (grp[fro[i]]!=grp[to[i]])
80         {
81             Add(grp[fro[i]],grp[to[i]]);
82             ++deg[grp[to[i]]];
83         }
84     }
85     for (int i=1;i<=tot;++i) if (!deg[i]) SPFA(i);
86     printf("%d\n",ans);
87     return 0;
88 }
View Code

 

posted @ 2017-11-07 20:57  氟铷氡氦  阅读(144)  评论(0编辑  收藏  举报