• 【UVA 11865】 Stream My Contest (二分+MDST最小树形图)


    【题意】

      你需要花费不超过cost元来搭建一个比赛网络。网络中有n台机器,编号0~n-1,其中机器0为服务器,其他机器为客户机。一共有m条可以使用的网线,其中第i条网线的发送端是机器ui,接收端是机器vi(数据只能从机器ui单向传输到机器vi),带宽是bi Kbps,费用是ci元。每台客户机应当恰好从一台机器接收数据。你的任务是最大化网络中的最小带宽。

    Input
    First line of input contains T (≤ 50), the number of test cases. This is followed by T test cases.
    Each test case starts with an integer N, M, C (1 ≤ N ≤ 60, 1 ≤ M ≤ 10^4, 1 ≤ C ≤ 10^9), the
    number of universities and the number of possible links, and the budget for setting up the network.
    Each university is identified by an integer between 0 and N − 1, where 0 is the server.
    Next M lines each contain 4 integers, u, v, b, c (0 ≤ u, v < N, 1 ≤ b, c ≤ 10^6, u ̸= v), describing
    a possible link from university u to university v, that has the bandwidth of b kbps and of cost c. All
    links are unidirectional.
    There is a blank line before each test case.
    Output
    For each test case, output the maximum bandwidth to stream.
    If it’s not possible, output ‘streaming not possible.’.
    Sample Input
    3
    3 4 300
    0 1 128 100
    1 2 256 200
    2 1 256 200
    0 2 512 300
    3 4 500
    0 1 128 100
    1 2 256 200
    2 1 256 200
    0 2 512 300
    3 4 100
    0 1 128 100
    1 2 256 200
    2 1 256 200
    0 2 512 300
    Sample Output
    128 kbps
    256 kbps
    streaming not possible.

    【分析】

      一开始看错题了ORZ,直接无视有向图,直接二分+MST。。

       好吧看过来之后就是多了个方向而已。。

      那就是MDST,要用朱-刘算法,,中国人发明的吧??

      hhhh

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<iostream>
      5 #include<algorithm>
      6 #include<queue>
      7 using namespace std;
      8 #define Maxn 110
      9 #define Maxm 10010
     10 #define INF 0xfffffff
     11 
     12 int n,m,mc;
     13 
     14 struct node
     15 {
     16     int x,y,b,c;
     17 };
     18 node t[Maxm],tt[Maxm];
     19 
     20 bool cmp(node x,node y) {return x.c<y.c;}
     21 int mymin(int x,int y) {return x<y?x:y;}
     22 int mymax(int x,int y) {return x>y?x:y;}
     23 
     24 int fa[Maxn];
     25 
     26 int ffa(int x)
     27 {
     28     if(fa[x]!=x) fa[x]=ffa(fa[x]);
     29     return fa[x];
     30 }
     31 
     32 int in[Maxn],pre[Maxn],id[Maxn],vis[Maxn];
     33 //in -> 最小入边 边权
     34 //pre -> 最小入边的发出点
     35 //id -> 所在圈的编号
     36 //vis 所在链顶端
     37 
     38 int rt;
     39 
     40 bool check(int mid) //MDST
     41 {
     42     rt=1;
     43     int ans=0;
     44     int nn=n;
     45     for(int i=1;i<=m;i++) tt[i]=t[i];
     46     while(1)
     47     {
     48         //1、找最小入边
     49         for(int i=1;i<=nn;i++) in[i]=INF;
     50         for(int i=1;i<=m;i++) 
     51         {
     52             int x=tt[i].x,y=tt[i].y;
     53             if(tt[i].c<in[y]&&x!=y&&tt[i].b>=mid) //因为缩边,所以可能有自环
     54             {
     55                 pre[y]=x;
     56                 in[y]=tt[i].c;
     57             }
     58         }
     59         //2、判断是否有最小生成树
     60         for(int i=1;i<=nn;i++) if(i!=rt)
     61         {
     62             if(in[i]==INF) return 0;
     63         }
     64         //3、找环
     65         int cnt=0;
     66         memset(id,-1,sizeof(id));
     67         memset(vis,-1,sizeof(vis));
     68         in[rt]=0;
     69         for(int i=1;i<=nn;i++)
     70         {
     71             ans+=in[i];
     72             int now=i;
     73             while(vis[now]!=i&&id[now]==-1&&now!=rt)
     74             {
     75                 vis[now]=i;
     76                 now=pre[now];
     77             }
     78             if(now!=rt&&id[now]==-1)
     79             {
     80                 cnt++;
     81                 for(int j=pre[now];j!=now;j=pre[j])
     82                 {
     83                     id[j]=cnt;
     84                 }
     85                 id[now]=cnt;
     86             }
     87         }
     88         if(cnt==0) break;//没有圈啦233
     89         for(int i=1;i<=nn;i++) if(id[i]==-1)
     90             id[i]=++cnt; //相当于自己一个圈
     91         //4、缩点,重新标记
     92         for(int i=1;i<=m;i++)
     93         {
     94             int x=tt[i].x,y=tt[i].y;
     95             tt[i].x=id[x];tt[i].y=id[y];
     96             if(tt[i].x!=tt[i].y)
     97             {
     98                 tt[i].c-=in[y];
     99             }
    100         }
    101         nn=cnt;
    102         rt=id[rt];
    103     }
    104     return ans<=mc;
    105 }
    106 
    107 void ffind(int l,int r)
    108 {
    109     int ans=-1;
    110     while(l<=r)
    111     {
    112         int mid=(l+r)>>1;
    113         if(check(mid)) ans=mid,l=mid+1;
    114         else r=mid-1;
    115     }
    116     if(ans==-1) printf("streaming not possible.
    ");
    117     else printf("%d kbps
    ",ans);
    118 }
    119 
    120 int main()
    121 {
    122     int T;
    123     scanf("%d",&T);
    124     while(T--)
    125     {
    126         scanf("%d%d%d",&n,&m,&mc);
    127         int mn=INF,mx=0;
    128         for(int i=1;i<=m;i++)
    129         {
    130             scanf("%d%d%d%d",&t[i].x,&t[i].y,&t[i].b,&t[i].c);
    131             t[i].x++;t[i].y++;
    132             mn=mymin(mn,t[i].b);
    133             mx=mymax(mx,t[i].b);
    134         }
    135         sort(t+1,t+1+m,cmp);
    136         ffind(mn,mx);
    137     }
    138     return 0; 
    139 }
    View Code

     // vis相当于一个不清0的bool

    2016-11-01 10:43:31


    记录一下MDST做法,求有向最小生成树,也叫最小树形图。

    主过程:

    1、给所有非根节点选一条权最小的入边。

    2、如果有一个点没有入边,那么无解。

    3、判断选出来的点是否构成圈,给每个点打上他所在的圈的编号标记(一定是i一个简单环,因为每个点只选一条入边)

    4、缩点,求出新的图。

    注意:

    第4步,入边的权值不是单纯的原图权值,还要减去圈里面和他矛盾的边的权值。

    时间复杂度:O(VE)

    如果一开始没有给你根怎么破???????????

    如果是不定根的情况我们可以虚拟一个根,让虚拟根到每个节点的距离为图上所有边的权值之和加一。这样找到最小树形图后再减掉所有边的权值之和加一就可以了。


    搜刮网上MDST的解释:

     

        图论是ACM竞赛中比较重要的组成部分,其模型广泛存在于现实生活之中。因其表述形象生动,思维方式抽象而不离具体,因此深受各类喜欢使劲YYAcmer的喜爱。这篇文章引述图论中有关有向图最小生成树的部分,具体介绍朱刘算法的求解思路,并结合一系列coding技巧,实现最小树型图OVE)的算法,并在最后提供了该算法的模版,以供参考。

      关于最小生成树的概念,想必已然家喻户晓。给定一个连通图,要求得到一个包含所有顶点的树(原图的子图),使之所构成的边权值之和最小。在无向图中,由于边的无向性质,所以求解变得十分容易,使用经典的kruskalprim算法已经足够解决这类问题。而在有向图中,则定义要稍微加上一点约束,即以根为起点,沿给定有向边,可以访问到所有的点,并使所构成的边权值之和最小,于是问题开始变得不一样了,我们称之为最小树型图问题。

      该问题是由朱永津与刘振宏在上个世纪60年代解决的,值得一提的是,这2个人现在仍然健在,更另人敬佩的是,朱永津几乎可以说是一位盲人。解决最小树型图的算法后来被称作朱刘算法,也是当之无愧的。

      首先我们来阐述下算法的流程:算法一开始先判断从固定根开始是否可达所有原图中的点,若不可,则一定不存在最小树形图。这一步是一个很随便的搜索,写多搓都行,不加废话。第二步,遍历所有的边,从中找出除根结点外各点的最小入边,累加权值,构成新图。接着判断该图是否存在环。若不存在,则该图便是所求最小树型图,当前权为最小权。否则对环缩点,然后回到第二步继续判断。

      这里存在一系列细节上的实现问题,以确保能够达到VE的复杂度。首先是查环,对于新图来说只有n-1条入边,对于各条入边,其指向的顶点是唯一的,于是我们可以在边表中添加from,表示该边的出发点,并考虑到如果存在环,则对环上所有边反向,环是显然同构的,于是最多作Vdfs就能在新图中找到所有的环,并能在递归返回时对顶点重标号进行缩点,此步的重标号可以用hash数组映射。然后就是重要的改边法,对于所有不在环上的边,修改其权为w-min(v)w为当前边权,min(v)为当前连接v点的最小边权。其数学意义在于选择当前边的同时便放弃了原来的最小入边。我们可以知道,每次迭代选边操作O(E),缩点操作O(V),更新权操作O(E),至少使一个顶点归入生成树,于是能在V-1步内完成计算,其复杂度为O(VE)

    以上为定根最小树型图,对于无固定根最小树型图,只要虚拟一个根连所有的点的权为边权总和+1,最后的结果减去(边权+1)即可。另外对于求所定的根标号,只要搜索被选中的虚边就可以判断了。

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cmath>
     4 #include <cstring>
     5 
     6 using namespace std;
     7 
     8 const int N=101,M=10001,inf=2147483647;
     9 struct edge {int u,v,w;} e[M];
    10 int n,m,ans,id[N],pre[N],v[N],inw[N];
    11 
    12 void zhu_liu(int root)
    13   {
    14     int s,t,idx=n;
    15     while (idx)
    16       {
    17         for (int i=1;i<=n;++i) inw[i]=inf,id[i]=-1,v[i]=-1;
    18         for (int i=1;i<=m;++i)
    19           {
    20             s=e[i].u;t=e[i].v;
    21             if (e[i].w>inw[t] || s==t) continue;
    22             pre[t]=s;
    23             inw[t]=e[i].w;
    24           }
    25         inw[root]=0;pre[root]=root;
    26         for (int i=1;i<=n;++i)
    27           {
    28             if (inw[i]==inf)
    29               {
    30                 printf("impossible
    ");
    31                 return;
    32               }
    33             ans+=inw[i];
    34           }
    35         idx=0;
    36         for (int i=1;i<=n;++i)
    37           if (v[i]==-1)
    38             {
    39               t=i;
    40               while (v[t]==-1) v[t]=i,t=pre[t];
    41               if (v[t]!=i || t==root) continue;
    42               id[t]=++idx;
    43               for (s=pre[t];s!=t;s=pre[s]) id[s]=idx;
    44             }
    45         if (idx==0) continue;
    46         for (int i=1;i<=n;++i)
    47           if (id[i]==-1) id[i]=++idx;
    48         for (int i=1;i<=m;++i)
    49           {
    50             e[i].w-=inw[e[i].v];
    51             e[i].u=id[e[i].u];
    52             e[i].v=id[e[i].v];
    53           }
    54         n=idx;
    55         root=id[root];
    56       }
    57     printf("%d
    ",ans);
    58   }
    59 int main()
    60   {
    61     scanf("%d%d",&n,&m);
    62     for (int i=1;i<=m;++i)
    63       scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    64     zhu_liu(1);
    65     return 0;
    66   }
    View Code

     

     
  • 相关阅读:
    20145318 《信息安全系统设计基础》第6.5周学习总结
    20145318 《信息安全系统设计基础》第6周学习总结
    20145318 《信息安全系统设计基础》第5.5周学习总结
    20145318 《信息安全系统设计基础》第5周学习总结
    20145318 《信息安全系统设计基础》第3周学习总结
    20145318 《信息安全系统设计基础》第2周学习总结
    20145318 《信息安全系统设计基础》第1周学习总结
    20145318 《信息安全系统设计基础》第0周学习总结
    20145318赵一Java课程总结
    20145317《信息安全系统设计基础》第10周学习总结2
  • 原文地址:https://www.cnblogs.com/Konjakmoyu/p/6018626.html
Copyright © 2020-2023  润新知