P3387 【模板】缩点 题解(tarjan缩点+拓扑排序+记忆化搜素dp)

题目链接

题意:给一个有向有环图,图上每个点有权值。找出一条路径点权值之和最大,注意每个点的权值只计算一次。

题解 :先将成环的强联通分量缩点,再用拓扑排序的顺序进行记忆化搜素就行了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4+100;
int n,m;
struct edge{
	int v,next;
}e[maxn*10];

int a[maxn],head[maxn],low[maxn],dfn[maxn],col[maxn],sum[maxn],s[maxn],vis[maxn];
int tot,cnt,colnum,idx,in[maxn],d[maxn];
void insert(int u,int v){
	e[++tot].v=v;e[tot].next=head[u];head[u]=tot;
}
vector<int>g[maxn];
void tarjan(int u){
	dfn[u]=low[u]=++cnt;
	s[++idx]=u;
	vis[u]=1;
	for(int i=head[u];i;i=e[i].next){
		int v = e[i].v; 
		if(!dfn[v]){
			tarjan(v);
			low[u] = min(low[u],low[v]);
		}
		else if(vis[v]) low[u] = min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		colnum++;
		do{
			col[s[idx]]=colnum;
			sum[colnum]+=a[s[idx]];
			vis[s[idx--]]=0;
		}while(u!=s[idx+1]);
	}
}
int topu(){
	queue<int>q;
	for(int i=1;i<=colnum;i++) 
	if(!in[i]) {
	   q.push(i);
	   d[i]=sum[i];	
	}
	while(!q.empty()){
		int t = q.front();q.pop();
		for(int i=0;i<g[t].size();i++){
			int v = g[t][i];
			d[v] = max(d[v],d[t]+sum[v]);
			in[v]--;
			if(!in[v]) q.push(v);
		}
	}
	int ans = 0;
	for(int i=1;i<=colnum;i++) ans = max(ans,d[i]);
	return ans;
 }
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=m;i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		insert(u,v);
	}
	
	for(int i=1;i<=n;i++) 
	  if(!dfn[i]) tarjan(i);
	  
	for(int i=1;i<=n;i++){
		for(int j=head[i];j;j=e[j].next){
			if(col[i]!=col[e[j].v]){
				g[col[i]].push_back(col[e[j].v]);
				in[col[e[j].v]]++;
			}
		}
	}
	printf("%d\n",topu());
}

猜你喜欢

转载自blog.csdn.net/qq_42129242/article/details/91357086
今日推荐