1. 程式人生 > >【LuoguP2792 】[JSOI2008]小店購物(最小樹形圖)

【LuoguP2792 】[JSOI2008]小店購物(最小樹形圖)

電腦 存在 簡單 要求 main jsoi2008 bit 最小 bool

題目鏈接

題目描述

小店的優惠方案十分簡單有趣:

一次消費過程中,如您在本店購買了精制油的話,您購買香皂時就可以享受2.00元/塊的優惠價;如果您在本店購買了香皂的話,您購買可樂時就可以享受1.50元/聽的優惠價......諸如此類的優惠方案可概括為:如果您在本店購買了商品A的話,您就可以以P元/件的優惠價格購買商品B(購買的數量不限)。

有趣的是,你需要購買同樣一些商品,由於不同的買賣順序,老板可能會叫你付不同數量的錢。比如你需要一塊香皂(原價2.50元)、一瓶精制油(原價10.00元)、一聽可樂(原價1.80元),如果你按照可樂、精制油、香皂這樣的順序購買的話,老板會問你要13.80元;而如果你按照精制油、香皂、可樂這樣的順序購買的話,您只需付13.50元。

該處居民發現JSOI集訓隊的隊員均擅長電腦程序設計,於是他們請集訓隊的隊員編寫一個程序:在告訴你該小店商品的原價,所有優惠方案及所需的商品後,計算至少需要花多少錢(不允許購買任何不必要的商品,即使這樣做可能使花的錢更少)。

Sol

首先不用買的全部丟掉 , 優惠活動也不用管。
考慮一個最優策略 , 顯然我們不會蠢到白白的優惠不要 , 那麽一定是先所有要買的物品都買一個 , 然後就能取得所有優惠。直接取最小值當做價格即可。

那麽問題就是讓所有物品買一次後總價格最小。
這就很像一個生成樹了。
由於有些物品可能沒有優惠 , 我們建立一個超級點 , 向所有其他物品連原價的邊 , 相當於買了超級點其他物品都能以原價的優惠購買。

這樣就是要求一個以超級點為根的最小樹形圖了 , 朱劉算法即可。

簡單說明朱劉算法的求解步驟:

首先要明確的一點是每一個點只會有一條入邊。

  1. 為所有點找到一個最小的入邊。
  2. 把這些邊的權值加入答案 , 然後判斷圖中是否存在環。
  3. 不存在環那麽算法結束 , 否則縮環(直接往入邊方向跳即可,不要tarjan) , 並把所有不在環內邊的邊的權值減去指向點的最小入邊(之前的環不合法 , 其中要去掉一條邊 , 這裏減去原入邊邊權就是在之後選擇新的邊的時候考慮了這個權值的變化)

本題code:

#include<bits/stdc++.h>
using namespace std;
typedef double db;
const int M=6000;
const int N=51;
struct edge{
    int u,v;db val;
    edge(){u=v=0,val=0.0;}
    edge(int a,int b,db c){u=a,v=b,val=c;}
}a[M],b[M];
int In[N],vis[N],bel[N],cnt=0,m,n,bcc;
db cost[N];int ned[N];
db Mi[N];
int rt;db ans=0;
int flag=0;
bool MST(){
    for(int i=1;i<=n;++i) In[i]=vis[i]=bel[i]=0;bcc=0,cnt=0;
    for(int i=1;i<=m;++i) {
        int u=a[i].u,v=a[i].v;db w=a[i].val;
        if(!In[v]||a[In[v]].val>w) In[v]=i;
    }
    for(int i=flag;i<=n;++i) {
        if(!flag&&!ned[i]) continue;
        if(i!=rt) ans+=a[In[i]].val;
        if(vis[i]) continue;
        int u=i;
        for(;u^rt&&vis[u]!=i&&!bel[u];u=a[In[u]].u) vis[u]=i;
        if(u^rt&&!bel[u]) {
            ++bcc;while(bel[u]!=bcc) bel[u]=bcc,u=a[In[u]].u;
        }
    }
    if(!bcc) return 0;
    for(int i=flag;i<=n;++i) {
        if(!flag&&!ned[i]&&i!=rt) continue;
        if(!bel[i]) bel[i]=++bcc;
    }
    for(int i=1;i<=m;++i) {
        int u=a[i].u,v=a[i].v;db w=a[i].val;
        if(bel[u]==bel[v]) continue;
        db goi=a[In[v]].val;b[++cnt]=(edge){bel[u],bel[v],w-goi};
    }
    for(int i=1;i<=cnt;++i) a[i]=b[i];
    n=bcc;m=cnt;
    return 1;
}
int main()
{
    cin>>n;int tot=n;// !! 註意記錄啊
    for(int i=1;i<=n;++i) cin>>cost[i]>>ned[i],Mi[i]=cost[i];
    cin>>m;
    for(int i=1;i<=m;++i) {
        int A,B;db P;
        cin>>A>>B>>P;
        if(!ned[A]||!ned[B]) --i,--m;
        else {Mi[B]=min(Mi[B],P);a[i]=edge(A,B,P);}
    }
    for(int i=1;i<=n;++i) if(ned[i]) a[++m]=edge(0,i,cost[i]);
    rt=0;flag=0;
    while(MST()) rt=bel[rt],flag=1;
    for(int i=1;i<=tot;++i) {
        if(ned[i]) {
            --ned[i];
            ans+=Mi[i]*(db)ned[i];
        }
    }
    printf("%.2lf\n",ans);
}

【LuoguP2792 】[JSOI2008]小店購物(最小樹形圖)