打卡 day 9
看题意就很好理解什么叫缩点,有一定的引导作用。
缩点就是把强连通分量(一个环)缩成一个点,因为要找最长一条路的权值,而且可以重复走,我们就可以把一个强连通分量内的权值都累加到它们的根的权值上,然后在新建的图中做广搜,或者拓扑排序+DP(我想先弄明白Tarjan,这个之后再补)
我这次把细节写在注释里吧。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m;
int dfn[maxn];//搜素次序编号——时间戳
int low[maxn];//回溯值,强连通的根
int belong[maxn];//属于哪个强连通分量,染色
int vis[maxn];//是否在栈中
int head[maxn];//
int sum[maxn];//权值和
int cnt,tot;
stack<int> s;//栈
struct node{
int to,next;
}edge[maxn];
void add(int u,int v){//在edge的结构体中存放u-->v(链表)
edge[cnt].to=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void tarjan(int x){
dfn[x]=low[x]=++tot;//初始化,每一个点的根是他自己
s.push(x);
vis[x]=1;
for(int i=head[x];~i;i=edge[i].next){
int v=edge[i].to;
if(!dfn[v]){//如果还有子节点
tarjan(v);//先深搜
low[x]=min(low[x],low[v]);//再回溯,回溯的时候x是当前节点,v是子节点,保证low[x]的值为当前最小
}else if(vis[v]){//v在栈中,v就是栈顶到v之间的点的强连通分量的根
low[x]=min(low[x],dfn[v]);//染色,让每个点回溯都赋值成根
}
}
if(low[x]==dfn[x]){//如果两者相等,x就是根
while(s.top()!=x){//出栈x之前的元素
int v=s.top();
s.pop();
sum[x]+=sum[v];//把权值累加给x--x是根
belong[v]=x;//染色
vis[v]=0;//出栈,v不在栈中
}
s.pop();
vis[x]=0;//出栈
belong[x]=x;
}
}
int u[maxn],v[maxn];
int dis[maxn];
int main(){
memset(head,-1,sizeof(head));//初始化,每一个点都是根
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&sum[i]);//输入自己的权值
}
for(int i=1;i<=m;i++){
scanf("%d%d",&u[i],&v[i]);//输入两点相连
add(u[i],v[i]);//连接两点,u --> v
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
tarjan(i);
}
}
cnt=0;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++){//两点有指向关系,缩点之后再连一次
if(belong[u[i]]!=belong[v[i]]){
add(belong[u[i]],belong[v[i]]);
vis[v[i]]++;
}
}
//下面是广搜了,不解释
int ans=0;
queue<int> q;
for(int i=1;i<=n;i++){
if(!vis[i]){
q.push(i);
dis[i]=sum[i];
}
}
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to;
dis[v]=max(dis[v],dis[u]+sum[v]);
q.push(v);
}
}
for(int i=1;i<=n;i++){
ans=max(ans,dis[i]);
}
printf("%d\n",ans);
//system("pause");
return 0;
}
大佬视频我还没看完,但感觉很不错。