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

不会最小树形图的出门左转
其实如果确定每种商品第一件的购买顺序,那么剩下的商品肯定是以最优惠价格购买的。
如何确定各种商品第一件购买时的最小价值呢?
考虑如果购买了\(a_i\)这种商品,那么就能以\(c_i\)的价格购买\(b_i\)这种商品,考虑从\(a_i\)\(b_i\)连权值为\(c_i\)的有向边。
初始建一个额外的\(S\)点往所有点i连权值为\(v_i\)的有向边。
然后用朱刘算法求一遍最小树形图就行了。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const double INF=1e9;
const int N=110;
const int M=10100;
int n,m,r,tru[N];
double ans,mn[N],c[N],num[N];
struct edge{
    int u,v;
    double w;
}e[M];
int cnt,id[N],top[N],fa[N];
bool work(){
    while(2333){
        memset(id,0,sizeof(id));
        memset(top,0,sizeof(top));
        for(int i=1;i<=n;i++)mn[i]=INF;
        for(int i=1;i<=m;i++)
            if(e[i].u!=e[i].v&&e[i].w<mn[e[i].v])
                fa[e[i].v]=e[i].u,mn[e[i].v]=e[i].w;
        mn[r]=0;fa[r]=0;
        for(int i=1;i<=n;i++)if(mn[i]==INF)return false;
        int u;
        for(int i=1;i<=n;i++){
            ans+=mn[i];
            for(u=i;u!=r&&top[u]!=i&&!id[u];u=fa[u])top[u]=i;
            if(u!=r&&!id[u]){
                id[u]=++cnt;
                for(int v=fa[u];v!=u;v=fa[v])id[v]=cnt;
            }
        }
        if(!cnt)return true;
        for(int i=1;i<=n;i++)if(!id[i])id[i]=++cnt;
        for(int i=1;i<=m;i++){
            double last=mn[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-=last;
        }
        n=cnt;
        r=id[r];
        cnt=0;
    }
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
signed main(){
    int tot=read();n=0;
    for(int i=1;i<=tot;i++){
        scanf("%lf",&c[++n]);
        num[n]=read()-1;
        if(num[n]<0){n--;continue;}
        tru[i]=n;
    }
    m=read();
    r=++n;
    for(int i=1;i<=n-1;i++)e[m+i].u=r,e[m+i].v=i,e[m+i].w=c[i];
    for(int i=1;i<=m;i++){
        int u=read(),v=read();
        double w;
        scanf("%lf",&w);
        if(tru[v]==0||tru[u]==0)continue;
        e[i].u=tru[u];e[i].v=tru[v];e[i].w=w;
        c[tru[v]]=min(c[tru[v]],w);
    }
    m+=n-1;
    for(int i=1;i<=n-1;i++)ans+=num[i]*c[i];
    if(work())printf("%.2lf",ans);
    else printf("-1");
    return 0;
}

转载于:https://www.cnblogs.com/Xu-daxia/p/10437070.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值