其他分享
首页 > 其他分享> > LightOJ 1251 Stream My Contest(最小树形图)

LightOJ 1251 Stream My Contest(最小树形图)

作者:互联网

最小树形图

个人理解就是求指定起点的有向图的最小生成树。

算法的大概步骤如下:

  1. 遍历所有边,求得一步到达点v的距离in[v]和前驱pre[v].(若除根节点外有的点不可以被到达则无解)
  2. 遍历所有点v,ans+in[v] (相当于从离v最近的点走到了v),看其是否在环上(一直跑pre,能跑到他自己说明在环上),若在环上则将环上的点缩成一个新点id[cnt++]
  3. 若一个环也没有找到,则最小树形图构建完成,返回ans
  4. 否则,将所有单点也构造成新点id[cnt++],更新边上点的编号和边权,若边的两端不在一个点边权要减去in[v] (2中ans已经加上in[v],及不管从那条边走到v,都已经走过in[v]了,至于在一个新点时,个人理解为环已经被缩成一个点,环上的边已经不会再走了)

LightOJ 1384

构建联通网络的花费就是最小树形图,二分最小的带宽然后重新建图,跑最小树形图是否小于最大花费.


#include<bits/stdc++.h>
#define ll long long
#define ls(r)   r<<1
#define rs(r)   r<<1|1
using namespace std;
const int N = 1e3+10;
const int INF = 0x3f3f3f3f;

struct edge{
    int u,v,w,cost;
    edge(int u,int v,int w,int cost):u(u),v(v),w(w),cost(cost){}
    edge(){}
};

struct directed_MT{
    int n,m;
    edge e[N*10];
    int vis[N],pre[N],id[N],in[N];  // 是否被访问过,前驱节点,新点编号,前驱到他的距离
    void init(int _n){n = _n; m = 0;}
    void add(int u,int v,int w){e[m++] = edge(u,v,w,0);}

    int dirMt(int sp){
        int ans = 0;
        while(1){
            for(int i=0;i<n;++i)    in[i] = INF;    // 初始化
            for(int i=0;i<m;++i){
                int u = e[i].u;
                int v = e[i].v;
                // 更新v的距离,和前驱
                if(e[i].w < in[v] && u!=v){
                    in[v] = e[i].w;
                    pre[v] = u;
                }                
            }
            for(int i=0;i<n;++i){
                if(i==sp) continue;
                // 无法到达 i 点 不连通
                if(in[i] == INF)    return -1;
            }
            int cnt = 0;    // 新图点的数量
            memset(id,-1,sizeof id);
            memset(vis,-1,sizeof vis);
            in[sp] = 0; // 初始点不要钱
            for(int i=0;i<n;++i){
                ans += in[i];   // 加上到这个点的距离
                int v = i;
                // 找自环
                while(vis[v]!=i && id[v]==-1 && v!=sp){
                    vis[v] = i;
                    v = pre[v];
                }
                if(v!=sp && id[v] ==-1){    // 缩点
                    for(int u=pre[v];u!=v;u=pre[u]) 
                        id[u] = cnt;
                    id[v] = cnt++;
                }
            }
// cerr << cnt << ' ' ;
            if(cnt==0)  break;  // 没有自环 算法截止
            for(int i=0;i<n;++i)    // 一个点构成一个环
                if(id[i]==-1)   id[i] = cnt++;
            for(int i=0;i<m;++i){  // 重新构图
                int v = e[i].v;
                e[i].v = id[e[i].v];
                e[i].u = id[e[i].u];
                if(e[i].v != e[i].u)    // 加不加貌似无所谓
                    e[i].w -= in[v];    // 已经可以到达v,所以到v的距离要减去
            }
            n = cnt;
            sp = id[sp];    // 更新节点个数和根节点
        }
        return ans;
    }
}MT;
edge e[N*10];
int n,m,c;
int check(int k){
    MT.init(n);
    for(int i=1;i<=m;++i){
        if(e[i].cost>=k)   MT.add(e[i].u,e[i].v,e[i].w);
    }
    int res =  MT.dirMt(0);
    return res <= c && res!=-1;
}

void solve(){
    scanf("%d%d%d",&n,&m,&c);
    MT.init(n);
    int u,v,w,cost;
    for(int i=1;i<=m;++i){
        scanf("%d%d%d%d",&u,&v,&w,&cost);
        e[i] = edge(u,v,cost,w);
    }
    int l=0,r=1e6+10;
    int mid,ans = -1;
    while(l<=r){
        mid = (l+r)>>1;
        if(check(mid)){
            ans = mid;
            l = mid + 1;
        }else{
            r = mid - 1;
        }
    }
    if(ans <= 0)   puts("impossible");
    else  printf("%d kbps\n",ans);
}
int main(){
    // freopen("1.out","w",stdout);
    int t; scanf("%d",&t);
    for(int i=1;i<=t;++i){
        printf("Case %d: ",i);
        solve();
    }
    return 0;
}

学习博客

标签:环上,Stream,Contest,LightOJ,最小,树形图,mid,ans
来源: https://www.cnblogs.com/xxrlz/p/11627511.html