我心中有无穷的问号(关于最小树形图)
查看原帖
我心中有无穷的问号(关于最小树形图)
239895
Yusani_huh楼主2022/7/15 10:34

这是一份 tarjin 求环的朱刘算法代码

#include<bits/stdc++.h>
using namespace std;
#define N 113
#define LL long long
#define INF 0x3f3f3f3f
int n,m,rt,pre[N],cnt,id[N];
bool vis[N],st[N];
LL ans,d[N][N],nd[N][N];
int tot,dfn[N],low[N],stk[N],tp;
void dfs(int u){
    vis[u]=true;
    for(int i=1;i<=n;++i)
        if(d[u][i]!=INF&&!vis[i]) dfs(i);
}
bool chk(){  //检查图是否连通
    memset(vis,0,sizeof vis);
    dfs(rt);
    for(int i=1;i<=n;++i)
        if(!vis[i]) return false;
    return true;
}
void taj(int u){  //tarjin求环并缩点
    dfn[u]=low[u]=++tot;
    stk[++tp]=u,st[u]=true;
    int v=pre[u];
    if(!dfn[v])
        taj(v),low[u]=min(low[u],low[v]);
    else if(st[v])
        low[u]=min(low[u],dfn[v]);
    if(low[u]==dfn[u]){
        cnt++;int y;
        do{y=stk[tp--],st[y]=false,id[y]=cnt;}
        while(y!=u);
    }
}
int main(){
	scanf("%d%d%d",&n,&m,&rt);
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j) d[i][j]=INF;
    for(int i=1;i<=m;++i){
    	int u,v;LL w;
        scanf("%d%d%lld",&u,&v,&w);
        if(u!=v&&v!=rt) d[u][v]=min(d[u][v],w);
	}
    if(!chk()) return (puts("-1"),0);
    while(1){  //朱刘算法主体
        for(int i=1;i<=n;++i){
            pre[i]=i;
            for(int j=1;j<=n;++j)
                if(d[j][i]<d[pre[i]][i])
                    pre[i]=j;
        }
        memset(dfn,0,sizeof dfn);
        tot=cnt=0;
        for(int i=1;i<=n;++i)
            if(!dfn[i]) taj(i);
        rt=id[rt];  //*1
        if(cnt==n){
            for(int i=1;i<=n;++i)
                ans+=d[pre[i]][i];  //*2
            break;
        }
        for(int i=1;i<=n;++i)
            if(id[pre[i]]==id[i])
                ans+=d[pre[i]][i];  //*3
        for(int i=1;i<=cnt;++i)
            for(int j=1;j<=cnt;++j)
                nd[i][j]=INF;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                if(d[i][j]!=INF&&id[i]!=id[j]){
                    int a=id[i],b=id[j];
                    if(id[pre[j]]==id[j]) nd[a][b]=min(nd[a][b],d[i][j]-d[pre[j]][j]);
                    else nd[a][b]=min(nd[a][b],d[i][j]);
                }
        n=cnt,memcpy(d,nd,sizeof d);
    }
    printf("%lld\n",ans%INF);  //*4
    return 0;
}

最开始建的图的时候判断 if(u!=v&&v!=rt) d[u][v]=min(d[u][v],w); 也就是如果这条边的终点为根那么将其忽略(因为答案中肯定不会有指向根的边)。

标有 *1 那行的代码将 rt 转为 id[rt],因为本来在 *2 *3 处有 i!=rt 的判断,也就是当枚举到的点为根时不统计答案,因为没有边指向根,d[pre[i]][i] 的结果肯定是 INF

现在的问题是我在任何一处加上判断,最终答案都不对,反而不加判断然后在最终结果上 %INF 过掉了(如果不加判断的话那么 *1 处实际是没有用的)。

于是尝试去掉 *1 处,在后两处加上判断,答案仍然不对。

于是我心中有无穷的问号。到底是我代码哪个部分出问题了,还是实际上就是这么个运行过程。

2022/7/15 10:34
加载中...