对朱刘算法求最小树形图的理解(uva11865)

来源:互联网 发布:mac 双定制粉底液 肤质 编辑:程序博客网 时间:2024/06/07 02:06

题目大意

一个图,0号节点是源点,每一条单向边有一个价值v和一个费用w,你只有C元钱,要使得源点和所有点都连通,且价值最小的边的价值最大。

题目分析

二分最小价值,只有比最小价值大的价值的边才能够加进去+最小树形图
最小树形图是什么呢?除了根节点以外,每一个点都有一个入度,且根节点可以到达所有节点的神奇图叫做树形图,而最小树形图,当然就是边权和最小咯!。

朱刘算法

步骤1

操作内容:贪心,给除了根节点以外的每一个点都找一个前驱节点,其中前驱节点到该节点的边是尽可能小的。如果某一个点没有入度,那么肯定就不存在树形图咯!

for(i=1;i<=m;++i){            if(e[i].v>=lim&&e[i].x!=e[i].y&&e[i].w<in[e[i].y])            in[e[i].y]=e[i].w,pre[e[i].y]=e[i].x;        }        for(i=1;i<=num;++i)if(!pre[i]&&i!=rt)return 0;        in[rt]=0,js=0;

步骤2

寻找简单环,因为我们已经找出前驱节点了,所以我们顺着前驱节点找简单环就可以了。如果没有简单环,那么就皆大欢喜,找到解了。如果有简单环呢?我们的目标是拆掉这个环中的一条边,然后连另一条入边进入这个点。这个方法可以用贪心的思想感受一下……
然后给每个简单环标号,以后它们是要被缩成一个点的。

for(i=1;i<=num;++i){            re+=in[i],t1=i;//re+=in[i]:这个计算答案的方法在步骤3有讲解            while(t1!=rt&&vis[t1]!=i&&!id[t1])vis[t1]=i,t1=pre[t1];            if(t1!=rt&&!id[t1]){//在t1处找到了简单环                t2=pre[t1],++js;                while(t2!=t1)id[t2]=js,t2=pre[t2];                id[t1]=js;//标号            }        }        if(!js){return re<=c;}//没有简单环,即找到结论        for(i=1;i<=num;++i)if(!id[i])id[i]=++js;

步骤3

缩点。
所有简单环看作一个点x,然后对于从环里走出的出边,就是x向外的出边,不用改变边权。对于入边,如果环外一点y到环内一点z之间有一条边权为w1的边,而如上面代码,in[y]=w2,则新边改成y到x的边,边权为w1-w2
为什么这么搞事呢?因为大家肯定注意到上面代码有这么一句:

re+=in[i];

所以说,我们这样只是减去原来选择的边造成的贡献,加上新边造成的贡献而已。
为了方便理解,给图如下:
图1
图2

for(i=1;i<=m;++i)if(e[i].v>=lim){            int kl=e[i].y;e[i].x=id[e[i].x],e[i].y=id[e[i].y];            if(e[i].x!=e[i].y)e[i].w-=in[kl];        }        rt=id[rt],num=js;

分段的代码可能有一些变量名没有解释清楚,那么看完整版代码吧。

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<climits>#include<cmath>using namespace std;#define LL long longconst int M=10005,N=65;int T,n,m,c;struct node{int x,y,v,w;}e[M],ee[M];int pre[N],vis[N],id[N],in[N];int ok(int lim){    int re=0,i,j,num=n,rt=1,js,t1,t2;    for(i=1;i<=m;++i)e[i]=ee[i];    while(1){        for(i=1;i<=num;++i)pre[i]=id[i]=vis[i]=0,in[i]=1e9;        for(i=1;i<=m;++i){            if(e[i].v>=lim&&e[i].x!=e[i].y&&e[i].w<in[e[i].y])//e[i].x!=e[i].y别忘了            in[e[i].y]=e[i].w,pre[e[i].y]=e[i].x;        }        for(i=1;i<=num;++i)if(!pre[i]&&i!=rt)return 0;        in[rt]=0,js=0;//步骤1        for(i=1;i<=num;++i){            re+=in[i],t1=i;            while(t1!=rt&&vis[t1]!=i&&!id[t1])vis[t1]=i,t1=pre[t1];            if(t1!=rt&&!id[t1]){//t1!                t2=pre[t1],++js;                while(t2!=t1)id[t2]=js,t2=pre[t2];                id[t1]=js;            }        }        if(!js){return re<=c;}        for(i=1;i<=num;++i)if(!id[i])id[i]=++js;//步骤2        for(i=1;i<=m;++i)if(e[i].v>=lim){//别忘了这句            int kl=e[i].y;e[i].x=id[e[i].x],e[i].y=id[e[i].y];            if(e[i].x!=e[i].y)e[i].w-=in[kl];        }        rt=id[rt],num=js;//步骤3    }}int main(){    int i,j,l,r,mid,ans;    scanf("%d",&T);    while(T--){        scanf("%d%d%d",&n,&m,&c);        l=0,r=0,ans=-1;        for(i=1;i<=m;++i){            scanf("%d%d%d%d",&ee[i].x,&ee[i].y,&ee[i].v,&ee[i].w);            ++ee[i].x,++ee[i].y;r=max(r,ee[i].v);        }        while(l<=r){//二分答案            mid=(l+r)>>1;            if(ok(mid))ans=mid,l=mid+1;            else r=mid-1;        }        if(ans==-1)printf("streaming not possible.\n");        else printf("%d kbps\n",ans);    }    return 0;}
阅读全文
0 0