LUOGU 2792: [JSOI2008]小店购物 最小树形图

title

LUOGU 2792

简化题意:

若干件物品,每个物品有一个原价,购买某件物品后可以以更优价购买另一件物品。每件物品有一个需求数目,既不能多买,也不能少买(如果需求 0 件就不能买,哪怕可能使得总价最优)。

analysis

这题有一个很棒的贪心算法,对于某件物品,我们怎样使得购买它的代价最小呢?

我们可以贪心的在这件物品所有的可行方案(原价与优惠价)里面取最小的,做一次乘法运算即可得出答案。

关键是,怎样使得这个贪心是正确的呢?我们发现,假如所有物品都被购买了的话,那么就可以保证这个贪心是正确的。

那么我们就可以把这个问题分成两个问题:

  1. 求出每个物品都买一件的最小代价和;
  2. 所有物品都购买了一件后,我们贪心算出剩余物品的代价和;

那么问题 1 怎么解决?

首先,我们对于每一个优惠方案 \((a,b,p)\),

  • 如果物品 \(a\) 不需要:那么我们从 \(a\) 到连 \(b\) 一条权值为 \(inf\) 的边。道理很好想,既然物品 \(a\) 不能选择,那么选择物品 \(a\) 的优惠方案肯定都是非法的,把权值设为 \(inf\) 就保证这个方案不会被选到;
  • 如果物品 \(b\) 不需要:那么我们从aa到 \(b\) 连一条权值为 \(0\) 的边。既然物品 \(b\) 不需要,我们可以看做购买物品 \(b\) 不需要任何代价;
  • 如果都需要,那就正常的从 \(a\) 到 \(b\) 连一条权值为 \(p\) 的边。

这样我们就处理完了优惠方案。

对于原价,我们新建一个虚拟节点,并从这个虚拟节点往所有点连一条边,如果需要,权值为这个物品的原价,否则为 \(0\) 。

这样原图就是一个以虚拟节点为根的有向图,然后我们发现这个问题有几条优美的性质:

  1. 有向图有根节点;
  2. 所有点都要被选到;
  3. 边权之和最小;

所以,就是最小树形图了,跑一遍就好了。

code

#include<bits/stdc++.h>

const int maxn=64,maxm=maxn*maxn;
const double inf=1e9;

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }
}

using IO::read;

template<typename T>inline bool chkMin(T &a,const T &b) { return a>b ? (a=b, true) : false; }
template<typename T>inline bool chkMax(T &a,const T &b) { return a<b ? (a=b, true) : false; }

int n,m,r;
double ans;
struct Orz{int x,y; double z;}e[maxm];
namespace Edmonds
{
    int pre[maxn];//fa[y] 当前连到 y 点的最短边的 x
    double In[maxn];//In[x] 为当前连到 x 点的最短边的边权
    int vis[maxn];//vis[x] 代表 x 所在链的代表元素,类似并查集
    int id[maxn];//id[x] 代表 x 节点在第 id[x] 个环中
    inline void zhu_liu()
    {
        while (1)
        {
            for (int i=1; i<=n; ++i) In[i]=inf;
            for (int i=1,x,y; i<=m; ++i)
            {
                x=e[i].x, y=e[i].y;
                if (x^y && e[i].z<In[y]) pre[y]=x, In[y]=e[i].z;//遍历所有边,对每个点找到最小的入边
            }

            int cnt=0;//cnt 代表当前图环的数量
            for (int i=1; i<=n; ++i) vis[i]=id[i]=0;
            for (int i=1; i<=n; ++i)
            {
                if (i==r) continue;
                ans+=In[i];
                int x=i;
                for ( ; x^r && vis[x]^i && !id[x]; x=pre[x]) vis[x]=i;//找环
                if (x^r && !id[x])
                {
                    id[x]=++cnt;//把环上的点标记为同一点
                    for (int y=pre[x]; y^x; y=pre[y]) id[y]=cnt;
                }
            }
            if (!cnt) break;//无环,得到解
            for (int i=1; i<=n; ++i)
                if (!id[i]) id[i]=++cnt;

            for (int i=1,x,y; i<=m; ++i)//建立新图,缩点,重新标记
            {
                x=e[i].x, y=e[i].y;
                if ( (e[i].x=id[x])^(e[i].y=id[y]) ) e[i].z-=In[y];//修改边权
            }
            n=cnt,r=id[r];
        }
    }
}

using Edmonds::zhu_liu;

double c[maxn];
int need[maxn],tot;
int main()
{
    read(n);
    for (int i=1; i<=n; ++i)
    {
        scanf("%lf",&c[i]), read(need[i]);
        e[++tot]=(Orz){n+1,i,!need[i] ? 0 : c[i]};
    }

    read(m);
    for (int i=1,a,b; i<=m; ++i)
    {
        read(a);read(b);
        double p;scanf("%lf",&p);
        if (!need[a]) e[++tot]=(Orz){a,b,inf};
        else if (!need[b]) e[++tot]=(Orz){a,b,0};
        else chkMin(c[b],p), e[++tot]=(Orz){a,b,p};
    }

    for (int i=1; i<=n; ++i) ans+=(need[i]-1)*c[i];
    r=n+1, m+=n, ++n;
    zhu_liu();
    printf("%.2lf\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/G-hsm/p/11407617.html

时间: 2024-08-29 15:50:25

LUOGU 2792: [JSOI2008]小店购物 最小树形图的相关文章

【LuoguP2792 】[JSOI2008]小店购物(最小树形图)

题目链接 题目描述 小店的优惠方案十分简单有趣: 一次消费过程中,如您在本店购买了精制油的话,您购买香皂时就可以享受2.00元/块的优惠价:如果您在本店购买了香皂的话,您购买可乐时就可以享受1.50元/听的优惠价......诸如此类的优惠方案可概括为:如果您在本店购买了商品A的话,您就可以以P元/件的优惠价格购买商品B(购买的数量不限). 有趣的是,你需要购买同样一些商品,由于不同的买卖顺序,老板可能会叫你付不同数量的钱.比如你需要一块香皂(原价2.50元).一瓶精制油(原价10.00元).一听

Luogu2792 JSOI2008 小店购物 最小树形图

传送门 被题意杀 本以为一个种类的物品一定要一起买 看了题解才知道可以先把所有要买的物品买一个,剩下要买的物品就可以得到这个种类的物品能够得到的最大优惠-- 所以现在只需要知道:第一次买所有物品一遍时按照什么顺序买最优惠 建一个超级源点向每一个物品连权值等同于其价值的边,对于优惠\((A,B,P)\)从\(A\)向\(B\)连权值为\(P\)的遍,然后一遍最小树形图即可. 注意一个购买数量为\(0\)的点和它的所有出入边都要被忽视 #include<bits/stdc++.h> //This

题解 P2792 【[JSOI2008]小店购物】

题目链接 Solution [JSOI2008]小店购物 题目大意:有若干件物品,每个物品有一个原价,购买某件物品后可以以更优价购买另一件物品.每件物品有一个需求数目,既不能多买,也不能少买(如果需求\(0\)件你就不能买,哪怕可能使得总价最优) 题目分析:看到题解区巨佬的题解,发现此题有一个绝妙的贪心做法. 对于某件物品,我们怎样使得购买它的代价最小呢?我们可以贪心的在这件物品所有的可行方案(原价与优惠价)里面取最小的,做一次乘法运算即可得出答案 关键是,怎样使得这个贪心是正确的呢?我们发现,

JSOI2008 小店购物

https://www.luogu.org/problem/show?pid=2792 题目背景 JSOI集训队的队员发现,在他们经常活动的集训地,有一个小店因为其丰富的经营优惠方案深受附近居民的青睐,生意红火. 题目描述 小店的优惠方案十分简单有趣: 一次消费过程中,如您在本店购买了精制油的话,您购买香皂时就可以享受2.00元/块的优惠价:如果您在本店购买了香皂的话,您购买可乐时就可以享受1.50元/听的优惠价......诸如此类的优惠方案可概括为:如果您在本店购买了商品A的话,您就可以以P元

bzoj2260: 商店购物 &amp;&amp; 4349: 最小树形图

Description Grant是一个个体户老板,他经营的小店因为其丰富的优惠方案深受附近居民的青睐,生意红火.小店的优惠方案十分简单有趣.Grant规定:在一次消费过程中,如果您在本店购买了精制油的话,您购买香皂时就可以享受2.00元/块的优惠价:如果您在本店购买了香皂的话,您购买可乐时就可以享受1.50元/听的优惠价……诸如此类的优惠方案就是说:如果您在本店购买了商品A的话,您就可以以P元/件的优惠价格购买商品B(购买的数量不限).有趣的是,你需要购买同样一些商品,由于不同的购买顺序,Gr

bzoj2260: 商店购物&amp;&amp;4349: 最小树形图

最小树形图问题啊 最小树形图是撒哩,就是给你一个有向图,确定一个根,要你到达所有点,那棵最短路径树的总边权 做这个用的是朱(jv)刘(lao)算法. 首先假如有多个联通块就无解啦 对应每个点(除了根),找到一条连向它的最短的边,假如没有环,那这个就是答案嘛 否则就找环,然后缩点,对于一个环,假如要从它的一个成员节点x断开,那么答案是减去环上的边,然后加上连进来的边,那么我们就把所有连向x的边的权,减去环上这条边的权(感觉很像数据备份退流的思想) 不断重复直到没有环. #include<cstdi

HDU2121 Ice_cream’s world II【最小树形图】【不定根】

Ice_cream's world II Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3005    Accepted Submission(s): 704 Problem Description After awarded lands to ACMers, the queen want to choose a city be he

前有淘宝后有“微信小店” 京东怎么玩拍拍?

浓缩观点 就在京东沉醉在微信开放了一级入口的时候,腾讯悄然上线了微信小店,这个无厘头的开幕,让准备在微信搭台唱戏的京东,顿时冷了下场. 我认为京东上市以来,干的最漂亮的一件事就是"重启"了拍拍网,即完成了腾讯交付的"政治任务",又很好地提升了股价,还顺带恶心了把淘宝,一箭三雕. 不过,好戏没玩,就在京东沉醉在微信开放了一级入口的时候,腾讯今天悄然上线了微信小店,这个无厘头的开幕,让准备在微信搭台唱戏的京东,顿时冷了下场. 且不说,微信小店能做成什么样,但是腾讯内部已

POJ--3164--Command Network【朱刘算法】最小树形图

链接:http://poj.org/problem?id=3164 题意:告诉n个点坐标,m条边表示两个点之间有路.从1点開始建立一个有向图最小生成树. 朱刘算法模板题 ========================== 切割线之下摘自user_id=Sasuke_SCUT" style="color:rgb(202,0,0); text-decoration:none; font-family:Arial; font-size:14px; line-height:26px"