zoukankan      html  css  js  c++  java
  • 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
    ",res);
        else{
            printf("%d
    ",res);
            for(int i=0;i<m;i++)
                if(usedE[i]&&edge[i].w) printf("%d ",i+1);
        }
        printf("
    ");
        
        return 0;
    } 

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

  • 相关阅读:
    CentOS6.5 安装 tomcat
    CentOS6.5 安装 jdk1.7
    CentOS6.5 mysql 5.5安装
    java使用split切割字符串的时候,注意转义字符
    hibernate中设置BigDeCimal的精度
    powerdesinger中建立一个表后,出现Existence of index的警告
    MySQL数据库一个字段对应多个值得模糊查询
    系统收到了多个不同的 Content-Disposition 标头。为了避免遭到 HTTP 响应拆分攻击,这种情况是不允许的。
    下载的文件名字当前时间(精确到秒)+ 随机数
    简单的poi导出excel文件
  • 原文地址:https://www.cnblogs.com/zsben991126/p/9809730.html
Copyright © 2011-2022 走看看