codeforce 240E 最小树形图+路径记录更新

最小树形图的路径是在不断建立新图的过程中更新的,因此需要开一个结构体cancle记录那些被更新的边,保存可能会被取消的边和边在旧图中的id

在朱刘算法最后添加了一个从后往前遍历新建边的循环,这可以理解为回溯,通过cancle结构体不断找到上一个时间点更新的边id,并且取消那些被代替的边

至于为什么要按建图时间从后往前回溯,我在下面举了一个例子:

 

 

上图取自朱刘算法标准示例,最小树形图路径保存与更新

拿节点v3举例

指向v3的边有三条:a4,a13,a9

第一次循环:步骤1,2建立最短弧集:a4被保存在最短弧集E中,usedE[4]=1

      步骤3:准备新建图,此时a9,a13权值被更新,其旧id被保存在cancle.id中,a4的id被保存在cancle.pre中,假设a9,a13被赋予新id a16,a17

第二次循环:步骤1, 2建立新图:a17被保存在新图中,usedE[17]=1

      步骤3:准备建立新图,a16被更新,假设其被赋予新id a18

第三次循环:没有环了,退出循环

 

退出循环后从后往前便利新建边,依旧拿v3举例

首先是循环到usedE[17]:cancle[17].id=13,因此usedE[13]=1

             cancle[17].pre=4,因此usedE[4]=0

最后在遍历被使用边时,可以发现被使用的是边a13,而a4被a13代替了

 

大家也可以拿其余点自己试试,下面贴上我的代码,codeforce240E,输入输出有点坑,需要从通过文件io

 

#include<iostream>
#include<cstring>
#include<cstdio>
#define MAXN 100005
#define MAXM MAXN*20
#define INF 0x3f3f3f3f
using namespace std;
struct Edge{
    int u,v,cost;
    int w;//原始权值 
    int id; 
}edge[MAXM];
inline void addedge(int u,int v,int cost,int w,int id){
    edge[id].cost=edge[id].w=cost;
    edge[id].u=u;
    edge[id].v=v;
    edge[id].id=id;
}
struct Cancle{//
    int pre;//保存可能被取消的那条边的id 
    int id;//保存可能新增的那条边更新前的id 
}cancle[MAXM];
int pre[MAXN],id[MAXN],vis[MAXN],in[MAXN];
int preid[MAXN],usedE[MAXN];
int zhuliu(int root,int n,int m){
    int res=0,total=m;//total是下一条新建边的id
    while(1){
        for(int i=0;i<n;i++) in[i]=INF;
        for(int i=0;i<m;i++)
            if(edge[i].u!=edge[i].v && edge[i].cost<in[edge[i].v]){
                pre[edge[i].v]=edge[i].u;
                in[edge[i].v]=edge[i].cost;
                //更新被加入到边集E的那条边的id 
                preid[edge[i].v]=edge[i].id;
            }
        for(int i=0;i<n;i++)
            if (i!=root && in[i]==INF) return -1;
        
        int tn=0;
        memset(id,-1,sizeof id);
        memset(vis,-1,sizeof vis);
        in[root]=0;
        for(int i=0;i<n;i++){
            res+=in[i];
            int v=i;
            ///将新图中被使用到的边保存
            if(i!=root) usedE[preid[i]]++; 
            while(v!=root && vis[v]!=i && id[v]==-1){
                vis[v]=i;
                v=pre[v];
            }
            if(v!=root && id[v]==-1){
                for(int u=pre[v];u!=v;u=pre[u])
                    id[u]=tn;
                id[v]=tn++;
            }
        } 
        if(tn==0) break;
        for(int i=0;i<n;i++) 
            if(id[i]==-1) id[i]=tn++;
        for(int i=0;i<m;i++){
            int v=edge[i].v;
            edge[i].u=id[edge[i].u];
            edge[i].v=id[edge[i].v];
            if(edge[i].u!=edge[i].v){
                edge[i].cost-=in[v];
                //把这条边的更新信息保存一下
                cancle[total].id=edge[i].id;//注意,这里是保留该边更新前的id! 
                cancle[total].pre=preid[v];//原本指向v的边被取消了 
                edge[i].id=total++;
            }
        }
        n=tn;
        root=id[root];
    } 
    /*
    为什么要从后往前? 
    */
    for(int i=total-1;i>=m;i--)
        if(usedE[i]){
            usedE[cancle[i].pre]--;
            usedE[cancle[i].id]++;
        }
    return res; 
} 
int main(){
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    
    int u,v,w;
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&u,&v,&w);
        u--,v--;
        addedge(u,v,w,w,i);
    }
    
    int root=0;
    int res=zhuliu(root,n,m);
    if(res==0||res==-1)
        printf("%d\n",res);
    else{
        printf("%d\n",res);
        for(int i=0;i<m;i++)
            if(usedE[i]&&edge[i].w) printf("%d ",i+1);
    }
    printf("\n");
    
    return 0;
} 

这段代码挂在了test31.。不知道为什么,望大佬指正,非常感谢!

posted on 2018-10-18 12:24  zsben  阅读(565)  评论(1编辑  收藏  举报

导航