1. 程式人生 > 實用技巧 >POJ1201 Intervals 差分約束(貪心也可)

POJ1201 Intervals 差分約束(貪心也可)

題面

給定 n 個區間 [ai,bi]和 n 個整數 ci。

你需要構造一個整數集合 Z,使得∀i∈[1,n],Z 中滿足ai≤x≤bi的整數 x 不少於 ci 個。

求這樣的整數集合 Z 最少包含多少個數。

輸入格式

第一行包含整數 n。

接下來n行,每行包含三個整數ai,bi,ci。

輸出格式

輸出一個整數表示結果。

資料範圍

1≤n≤50000,
0≤ai,bi≤50000,
1≤ci≤bi−ai+1

輸入樣例:

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

輸出樣例:

6

題解

差分約束

一堆不等式, 明顯是差分約束

設 s[i], 表示 0~i 區間選擇了 s[i] 個數

則 s[b] - s[a - 1] >= c

只是這樣的話, 不能將 -1(a >= 0, a - 1 >= -1)~5e4 每個節點連線起來

我們發現 s[i] - s[i - 1] >= 0, s[i - 1] - s[i] >= -1

這樣就將所有節點全部連線起來, 由於解的存在, 無負環,

對於差分約束, 要麼把所有點扔進佇列, 要麼選擇一個初始節點

對於這道題要麼把, 0~50000全扔進去, 要麼從 -1, s[-1] ≡ 0

由於越界, 我們將 -1 由 5e4 + 1代替, 或者整體將區間右移, 變為 0~50001

const int N = 5e4 + 5;
const double eps = 1e-6;

int n, m, _, k;
int h[N], ne[N << 2], to[N << 2], co[N << 2], tot;
int a[N], b[N], c[N], dis[N];
bool v[N];

void add(int u, int v, int c) {
    ne[++tot] = h[u]; co[h[u] = tot] = c; to[tot] = v;
}

void spfa(int s) {
    queue<int> q;
    memset(dis, -1, sizeof dis);
    q.push(s); dis[s] = 0;
    while (!q.empty()) {
        int x = q.front(); q.pop(); v[x] = 0;
        for (int i = h[x]; i; i = ne[i]) {
            int y = to[i];
            if (dis[y] >= dis[x] + co[i]) continue;
            dis[y] = dis[x] + co[i];
            if (!v[y]) q.push(y), v[y] = 1;
        }
    }
}

int main() {
    sc(n);
    rep(i, 1, 5e4) add(i - 1, i, 0), add(i, i - 1, -1);
    add(5e4 + 1, 0, 0); add(0, 5e4 + 1, -1);
    rep(i, 1, n) {
        sc(a[i]), sc(b[i]), sc(c[i]); --a[i];
        if (a[i] == -1) a[i] = 5e4 + 1;
        add(a[i], b[i], c[i]);
    }
    spfa(5e4 + 1);
    pr(dis[50000]);
    return 0;
}

貪心

將b按照升序排列, 儘量將數字靠近 b 放置, 加上樹狀陣列,並查集優化, 即可

const int N = 5e4 + 5;
const double eps = 1e-6;

int n, m, _, k;
int c[N], f[N];
pair<PII, int> p[N];

void add(int x, int k) {
    for (; x <= 5e4; x += -x & x) c[x] += k;
}

int ask(int x) {
    int ans = 0;
    for (; x; x -= -x & x) ans += c[x];
    return ans;
}

int find(int x) {
    if (x == f[x]) return x;
    return f[x] = find(f[x]);
}

int main() {
    sc(n);
    rep(i, 1, n) sc(p[i].fi.se), sc(p[i].fi.fi), sc(p[i].se);
    rep(i, 1, 5e4 + 1) f[i] = i;
    sort(p + 1, p + 1 + n);
    rep(i, 1, n) {
        int x = p[i].fi.se + 1, y = p[i].fi.fi + 1, cnt = p[i].se;
        cnt -= ask(y) - ask(x - 1);
        if (cnt <= 0) continue;
        for (int j = find(y); cnt; j = f[j])
            --cnt, add(j, 1), f[j] = find(j - 1), ++m;
    }
    pr(m);
    return 0;
}