POJ 1201 差分约束系统 / 贪心 + BIT + 并查集

205 篇文章 3 订阅
203 篇文章 0 订阅
题意

传送门 POJ 1201 Intervals

题解
差分约束系统

间区间和转化为前缀和之差。设 S [ i ] S[i] S[i] 为区间 [ 0 , i ] [0,i] [0,i] 内数字的个数,则区间约数转化为 S [ b i ] − S [ a i − 1 ] ≥ c i S[b_i]-S[a_i-1]\geq c_i S[bi]S[ai1]ci,明显是一个差分约束系统的模型。不过,需要增加约数,以保证得到的解是有意义的。前缀和 S S S 单调不减,且长度为 1 1 1 的区间至多能选取 1 1 1 个数,即 S [ k ] − S [ k − 1 ] ≥ 0 , S [ k ] − S [ k − 1 ] ≤ 1 S[k]-S[k-1]\geq 0,S[k]-S[k-1]\leq1 S[k]S[k1]0,S[k]S[k1]1

差分约束系统可以固定某个变量的取值,这里为 S [ − 1 ] = 0 S[-1]=0 S[1]=0,求出满足不等式组的一组可行解。但这里需要求解最小值,则将不等式转化为 X i − X j ≥ c k X_i-X_j\geq c_k XiXjck 的形式,求解单源最长路;若需要求解最大值,则将不等式转化为 X i − X j ≤ c k X_i-X_j\leq c_k XiXjck,求解单源最短路。
{ S [ a i − 1 ] + c i ≤ S [ b i ] S [ k ] − 1 ≤ S [ k − 1 ] S [ k − 1 ] ≤ S [ k ] \begin{cases}S[a_i-1]+c_i\leq S[b_i]\\ S[k]-1\leq S[k-1]\\ S[k-1]\leq S[k] \\ \end{cases} S[ai1]+ciS[bi]S[k]1S[k1]S[k1]S[k] S P F A SPFA SPFA 求解,总时间复杂度 O ( N 2 ) O(N^2) O(N2)

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 50005, maxm = 3 * maxn, inf = 0x3f3f3f3f;
int N, lim, ds[maxn];
int tot, head[maxn], to[maxm], cost[maxm], nxt[maxm];
bool in[maxn];

inline void add(int x, int y, int z)
{
    to[++tot] = y, cost[tot] = z, nxt[tot] = head[x], head[x] = tot;
}

void SPFA(int s)
{
    for (int i = 0; i <= lim; ++i)
        ds[i] = -inf, in[i] = 0;
    ds[s] = 0, in[s] = 1;
    queue<int> q;
    q.push(s);
    while (q.size())
    {
        int x = q.front();
        q.pop(), in[x] = 0;
        for (int i = head[x]; i; i = nxt[i])
        {
            int y = to[i], z = cost[i];
            if (ds[x] + z > ds[y])
            {
                ds[y] = ds[x] + z;
                if (!in[y])
                    in[y] = 1, q.push(y);
            }
        }
    }
}

int main()
{
    scanf("%d", &N);
    lim = 0;
    for (int i = 1, a, b, c; i <= N; ++i)
        scanf("%d%d%d", &a, &b, &c), lim = max(lim, b + 1), add(a, b + 1, c);
    for (int i = 1; i <= lim; ++i)
        add(i, i - 1, -1);
    for (int i = 1; i <= lim; ++i)
        add(i - 1, i, 0);
    SPFA(0);
    printf("%d\n", ds[lim]);
    return 0;
}
贪心 + BIT + 并查集

容易得到贪心策略,将三元组 ( a , b , c ) (a,b,c) (a,b,c) 按照 b b b 升序排序,顺序处理各个区间,尽可能地取靠后的数字,使之满足条件。

使用 B I T BIT BIT 维护区间和。使用并查集维护区间中还未取到的最大元素,具体而言,若取数字 i i i,则将 i i i 的父节点取为 i − 1 i-1 i1,此时根节点就是区间中还未取到的最大元素。总时间复杂度 O ( N log ⁡ N ) O(N\log N) O(NlogN)

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 50005;
struct node
{
    int a, b, c;
    bool operator<(const node &o) const { return b < o.b; }
} A[maxn];
int N, lim, bit[maxn], fa[maxn];

int find(int x) { return fa[x] == x ? x : (fa[x] = find(fa[x])); }

void add(int i)
{
    while (i <= lim)
        ++bit[i], i += i & -i;
}

int sum(int i)
{
    int s = 0;
    while (i)
        s += bit[i], i -= i & -i;
    return s;
}

int main()
{
    scanf("%d", &N);
    for (int i = 1, a, b, c; i <= N; ++i)
        scanf("%d%d%d", &a, &b, &c), ++a, ++b, lim = max(lim, b), A[i] = node{a, b, c};
    sort(A + 1, A + N + 1);
    for (int i = 1; i <= lim; ++i)
        fa[i] = i;
    for (int i = 1; i <= N; ++i)
    {
        int t = A[i].c - (sum(A[i].b) - sum(A[i].a - 1)), j = A[i].b;
        while (t > 0)
            j = find(j), add(j), --t, fa[j] = j - 1;
    }
    printf("%d\n", sum(lim));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值