bzoj4349&2260 最小树形图(商店购物)(朱刘算法 最小树形图)

83 篇文章 0 订阅

bzoj4349&2260 最小树形图(商店购物)

原题地址
http://www.lydsy.com/JudgeOnline/problem.php?id=4349
http://www.lydsy.com/JudgeOnline/problem.php?id=2260

题意:
小C现在正要攻打科学馆腹地——计算机第三机房。而信息组的同学们已经建好了一座座堡垒,准备迎战。小C作为一种高度智慧的可怕生物,早已对同学们的信息了如指掌。
攻打每一个人的堡垒需要一个代价,而且必须攻打若干次才能把镇守之人灭得灰飞烟灭。
当小C在绞尽脑汁想攻打方案时,突然从XXX的堡垒中滚出来一个纸条:一个惊人的秘密被小C发现了:原来各个堡垒之间会相互提供援助,但是当一个堡垒被攻打时,他对所援助的堡垒的援助就会停止,因为他自己已经自身难保了。也就是说,小C只要攻打某个堡垒一次之后,某些堡垒就只需要花更小的代价攻击了。
现在,要你求消灭全机房要用掉代价最小多少。

数据范围
N<=10000,T<=7

题解:

裸题。
这里写图片描述
复杂度O(VE)


最小树形图当没有固定根时,新建虚根,向每个点连边权为inf( +1 )的边,如果发现把inf的边计入答案了,那么无解。


代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=105;
const int M=20005;
const double inf=1e50;
struct node
{
    int u,v; double w;
    node(){}
    node(int u,int v,double w):u(u),v(v),w(w){}
}E[M];
double c[N],in[N];
int n=0,m=0,k=0,cnt=0,root,vis[N],id[N],num[N],tail=1,pre[N];
double solve()
{
    double ret=0;
    while(1)
    {
        for(int i=1;i<=n;i++){vis[i]=0; id[i]=0; in[i]=inf;}
        for(int i=1;i<=m;i++)
        {
            if(E[i].u!=E[i].v&&E[i].w<in[E[i].v])
            {in[E[i].v]=E[i].w; pre[E[i].v]=E[i].u;}
        }
        in[root]=0;
        for(int i=1;i<=n;i++) if(in[i]>=inf) return -1;
        int tot=0;
        for(int i=1;i<=n;i++)
        {
            ret+=in[i];int j;
            for(j=i;vis[j]!=i&&j!=root&&!id[j];j=pre[j]) vis[j]=i;
            if(j!=root&&!id[j])
            {
                tot++;
                for(int k=j;!id[k];k=pre[k]) id[k]=tot;
            }
        }
        if(!tot) return ret;
        for(int i=1;i<=n;i++) if(!id[i]) id[i]=++tot;
        for(int i=1;i<=m;i++)
        {
            int u=E[i].u; int v=E[i].v;
            E[i].u=id[E[i].u]; E[i].v=id[E[i].v];
            if(E[i].u!=E[i].v) E[i].w-=in[v];
        }
        n=tot; root=id[root];
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
    {
        double a; int b;
        scanf("%lf%d",&a,&b);
        if(b) {++m; E[m]=node(0,m,a); c[m]=a; num[m]=b; id[i]=m;}
    }
    root=n=m+1; cnt=m;
    for(int i=1;i<=cnt;i++) E[i].u=n;
    scanf("%d",&k);
    for(int i=1;i<=k;i++)
    { 
        int u,v; double ww; scanf("%d%d%lf",&u,&v,&ww);
        if(id[u]&&id[v]) 
        {
            E[++m]=node(id[u],id[v],ww);
            c[id[v]]=min(c[id[v]],ww);
        }
    }
    double ans=solve();
    for(int i=1;i<=cnt;i++) ans+=(double)(num[i]-1)*c[i];
    printf("%0.2lf\n",ans);
    return 0;
}       
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值