最小樹形圖--朱劉算法([JSOI2008]小店購物)


題面

luogu

Sol

首先設一個 \(0\) 號點,向所有點連邊,表示初始價值
顯然這個圖的一個 \(0\) 為根的最小有向生成樹的邊權和就是每個買一次的最小價值
再買就一定能優惠(包含 \(0\) 的邊)

有向圖最小生成樹???

朱劉算法

其實正確性不會理論。。
可以說是一個不斷調整的過程,從而得到最優解

時間復雜度 \(O(VE)\)

流程:

1.去掉自環
2.先給每個點選擇一條最小的入邊,並記錄連過來的點
3.如果此時有點沒有入邊(除根以外),那么顯然無解
4.算上每個點入邊貢獻,加入答案
5.如果沒有有向環,那么做完結束
6.如果有,縮點,並且把連出去的邊都減去連出去那個點的入邊,因為貢獻算過了
7.存儲新圖,對新圖重復所有操作

Code

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;

const int maxn(100005);
const double inf(1e9);

int n, tot, m, cnt, need[maxn], pre[maxn], vis[maxn], id[maxn];
double ans, cost[maxn], prize, inw[maxn];

struct Edge{
    int u, v;
    double w;
} e[maxn];

IL void DirectedMST(){
    RG int num = n, rt = 1, idx;
    while(true){
        // 初始化
        for(RG int i = 1; i <= num; ++i) id[i] = vis[i] = pre[i] = -1, inw[i] = inf;
        // 選入邊
        for(RG int i = 1; i <= cnt; ++i)
            if(inw[e[i].v] > e[i].w && e[i].u != e[i].v) inw[e[i].v] = e[i].w, pre[e[i].v] = e[i].u;
        pre[rt] = rt, idx = inw[rt] = 0;
        // 縮環,統計貢獻
        for(RG int i = 1; i <= num; ++i){
            ans += inw[i];
            if(vis[i] == -1){
                RG int nw = i;
                while(vis[nw] == -1) vis[nw] = i, nw = pre[nw];
                if(vis[nw] == i && nw != rt){
                    id[nw] = ++idx;
                    for(RG int j = pre[nw]; j != nw; j = pre[j]) id[j] = idx;
                }
            }
        }
        // 沒有環結束
        if(!idx) return;
        // 重標號,記錄新圖
        for(RG int i = 1; i <= num; ++i) if(id[i] == -1) id[i] = ++idx;
        for(RG int i = 1; i <= cnt; ++i)
            e[i].w -= inw[e[i].v], e[i].u = id[e[i].u], e[i].v = id[e[i].v];
        num = idx, rt = id[rt];
    }
}

int main(){
    scanf("%d", &tot), n = 2;
    for(RG int i = 1; i <= tot; ++i){
        scanf("%lf%d", &cost[n], &need[n]);
        if(need[n]) e[++cnt] = (Edge){1, n, cost[n]}, vis[i] = n++;
    }
    --n, scanf("%d", &m);
    for(RG int i = 1, a, b; i <= m; ++i){
        scanf("%d%d%lf", &a, &b, &prize);
        a = vis[a], b = vis[b];
        if(a && b){
            cost[b] = min(cost[b], prize);
            e[++cnt] = (Edge){a, b, prize};
        }
    }
    for(RG int i = 2; i <= n; ++i) ans += (need[i] - 1) * cost[i];
    DirectedMST();
    printf("%.2lf\n", ans);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM