您的位置:首页 > 其它

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

2017-09-29 21:57 197 查看

题目大意

一个图,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];


所以说,我们这样只是减去原来选择的边造成的贡献,加上新边造成的贡献而已。

为了方便理解,给图如下:





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 long
const int M=10005,N=65;
int T,n,m,c;
struct node{int x,y,v,w;}e[M],ee[M];
int pre
,vis
,id
,in
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: