洛谷P2812 校园网络
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct arr{
int nd,nx;
}bot[1100000],d[1100000];
int head[20000],h[20000],stack[20000],low[20000],dfn[20000],bl[20000];
int rd[20000],cd[20000],tot,cnt,pos,top,scc,size[20000],n;
bool instack[20000];
inline int read(){
int x=0,w=1;char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
return x*w;
}
inline void add(int a,int b){bot[++cnt].nd=b;bot[cnt].nx=head[a];head[a]=cnt;}
inline void ad1(int a,int b){d[++tot].nd=b;d[tot].nx=h[a];h[a]=tot;rd[b]++;cd[a]++;}
inline void tarjan(int u){
dfn[u]=low[u]=++pos;
stack[++top]=u;instack[u]=true;
for(register int i=head[u];i;i=bot[i].nx) {
int v=bot[i].nd;
if(instack[v]) low[u]=min(dfn[v],low[u]);
if(dfn[v]==0) {tarjan(v);low[u]=min(low[v],low[u]);}
}
if(low[u]==dfn[u]){
++scc;int t=0;
while(u!=t)
{ t=stack[top--];instack[t]=false;bl[t]=scc;size[scc]++; }
}
}
int main(){
n=read();
for(register int i=1;i<=n;++i) {int x=read();while(x){ add(i,x); x=read(); }}
for(register int i=1;i<=n;++i) if(!dfn[i])tarjan(i);
tot=0;
for(register int i=1;i<=n;++i)
for(register int j=head[i];j;j=bot[j].nx){
int v=bot[j].nd;
if(bl[i]!=bl[v]) ad1(bl[i],bl[v]);
}
int ans1=0,ans2=0;
for(register int i=1;i<=scc;++i){//统计一下缩点之后的入度和出度
if(!rd[i]) ans1++;
if(!cd[i]) ans2++;
}
if(scc==1) printf("1\n0");
else printf("%d\n%d",ans1,max(ans1,ans2));
}
洛谷 P3387 缩点
一道模板题,就直接贴代码了。
下面这份代码是先tarjan缩点,再建立一个新图,然后加上SPFA
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct arr{
int nd,nx;
}bot[1100000],d[1100000];
int head[100000],h[100000],stack[100000],low[100000],dfn[100000],bl[100000];
int tot,cnt,pos,top,scc,m,n;
int c[100000],w[100000];
long long dist[100000],f[100000];
long long ans;
bool instack[100000];
inline int read(){
int x=0,w=1;char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
return x*w;
}
inline void add(int a,int b){bot[++cnt].nd=b;bot[cnt].nx=head[a];head[a]=cnt;}
inline void ad1(int a,int b){d[++tot].nd=b;d[tot].nx=h[a];h[a]=tot;}
inline void tarjan(int u){
dfn[u]=low[u]=++pos;
stack[++top]=u;instack[u]=true;
for(register int i=head[u];i;i=bot[i].nx){
int v=bot[i].nd;
if(!dfn[v]) { tarjan(v);low[u]=min(low[v],low[u]);}
if(instack[v]) low[u]=min(dfn[v],low[u]);
}
if(dfn[u]==low[u]){
int t=0;++scc;
while(u!=t){ t=stack[top--];instack[t]=false;bl[t]=scc;c[scc]+=w[t];}
}
}
inline void SPFA(int s){
deque<int>q;
for(int i=1;i<=n;i++) dist[i]=0,f[i]=0;
dist[s]=c[s];f[s]=1;
q.push_back(s);
while(!q.empty()){
int now=q.front();
q.pop_front();
f[now]=0;
for(register int i=h[now];i;i=d[i].nx){
int v=d[i].nd;
if(dist[v]<dist[now]+c[v]){
dist[v]=dist[now]+c[v];
if(!f[v]){
f[v]=1;
if(q.empty()||dist[v]>dist[q.front()]) q.push_back(v);
else q.push_front(v);
}
}
}
}
for(register int i=1;i<=scc;++i) if(ans<dist[i]) ans=dist[i];
}
int main(){
n=read(); m=read();
for(register int i=1;i<=n;++i) w[i]=read();
for(register int i=1;i<=m;++i) { int u=read(),v=read(); add(u,v); }
for(register int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
for(register int i=1;i<=n;++i)
for(register int j=head[i];j;j=bot[j].nx) {
int v=bot[j].nd;
if(bl[i]!=bl[v])
ad1(bl[i],bl[v]);
}
for(register int i=1;i<=scc;++i) SPFA(i);
//对于缩点后新建的图跑SPFA
printf("%d\n",ans);
}
下面这个代码是先tarjan缩点,建立一个新图,然后加上拓扑排序求最长路。
(本蒟一开始做的时候只有40分,后面请了机房某xxy大佬帮我改了下才过的)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
struct arr{
int nd,nx;
}bot[210000],d[210000];
int head[20000],a[20000],h[20000];
int rd[20000],cd[20000],dis[20000],topo[30000];
int low[20000],dfn[20000],stack[20000],instack[20000],bl[20000],size[20000],c[20000];
int n,m,cnt,tot,tt,top,pos,scc,ans;
inline int read(){
int x=0,w=1;char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch-48),ch=getchar();
return x*w;
}
inline void add(int a,int b) { bot[++cnt].nd=b; bot[cnt].nx=head[a]; head[a]=cnt; }
inline void ad1(int a,int b) { d[++tot].nd=b; d[tot].nx=h[a]; h[a]=tot;rd[b]++;cd[a]++;}
inline void tarjan(int u){
dfn[u]=low[u]=++pos;
stack[++top]=u; instack[u]=true;
for(register int i=head[u];i;i=bot[i].nx) {
int v=bot[i].nd;
if(!dfn[v]) { tarjan(v);low[u]=min(low[u],low[v]); }
if(instack[v]) {low[u]=min(dfn[v],low[u]);}
}
if(dfn[u]==low[u]) {
int t=0;++scc;
while(t!=u) { t=stack[top--]; instack[t]=false; bl[t]=scc; size[scc]++; c[scc]+=a[t];}
}
}
inline void tuopu(){
queue<int> q;
for(register int i=1;i<=scc;++i) if(!rd[i]) q.push(i);
while(!q.empty()) {
int u=q.front();q.pop();
topo[++tt]=u;
for(register int i=h[u];i;i=d[i].nx) {
int v=d[i].nd;--rd[v];
if(!rd[v]) q.push(v);
}
}
for(register int i=1;i<=scc;++i) dis[i]=c[i];
int ans=0;
for(int k=1;k<=tt;++k) {
int u=topo[k];
for(register int i=h[u];i;i=d[i].nx) {
int v=d[i].nd;
dis[v]=max(dis[v],dis[u]+c[v]);
}
}
for(register int i=1;i<=scc;++i) ans=max(ans,dis[i]);
printf("%d\n",ans);
}
int main(){
n=read();m=read();
for(register int i=1;i<=n;++i) a[i]=read();
for(register int i=1;i<=m;++i) {
int u=read(),v=read();
add(u,v);
}
for(register int i=1;i<=n;++i)if(!dfn[i]) tarjan(i);
for(register int i=1;i<=n;++i)
for(register int j=head[i];j;j=bot[j].nx) {
int v=bot[j].nd;
if(bl[i]!=bl[v]) { ad1(bl[i],bl[v]); }//建立新图的时候要注意加入的点是bl[i]和bl[v],不是i和v,错了好几次
}
tuopu();
return 0;
}

本文详细解析了洛谷P2812校园网络和P3387缩点两道算法题目,通过Tarjan算法进行缩点处理,并分别采用SPFA算法和拓扑排序求解最长路径问题。
394

被折叠的 条评论
为什么被折叠?



