【BZOJ2436】【NOI2011】NOI嘉年华(DP)

83 篇文章 0 订阅
21 篇文章 0 订阅

Description

NOI2011 在吉林大学开始啦!为了迎接来自全国各地最优秀的信息学选手,吉林大学决定举办两场盛大的 NOI 嘉年华活动,分在两个不同的地点举办。每个嘉年华可能包含很多个活动,而每个活动只能在一个嘉年华中举办。

现在嘉年华活动的组织者小安一共收到了 n个活动的举办申请,其中第 i 个活动的起始时间为 Si,活动的持续时间为Ti。这些活动都可以安排到任意一个嘉年华的会场,也可以不安排。

小安通过广泛的调查发现,如果某个时刻,两个嘉年华会场同时有活动在进行(不包括活动的开始瞬间和结束瞬间),那么有的选手就会纠结于到底去哪个会场,从而变得不开心。所以,为了避免这样不开心的事情发生,小安要求不能有两个活动在两个会场同时进行(同一会场内的活动可以任意进行)。

另外,可以想象,如果某一个嘉年华会场的活动太少,那么这个嘉年华的吸引力就会不足,容易导致场面冷清。所以小安希望通过合理的安排,使得活动相对较少的嘉年华的活动数量最大。

此外,有一些活动非常有意义,小安希望能举办,他希望知道,如果第i 个活动必须举办(可以安排在两场嘉年华中的任何一个),活动相对较少的嘉年华的活动数量的最大值。


Solution

先把时间离散掉。

fi,j f i , j 表示时间 1i 1 … i 、一个场地选了 j j 个活动,另一个场地最多可以选多少个活动;gi,j表示时间 itot i … t o t … …
转移显然,第一问的答案为 max(min(j,f[tot][j])) max ( min ( j , f [ t o t ] [ j ] ) )

第二问设 dpi,j d p i , j 表示 [i,j] [ i , j ] 必选的最优值。我们枚举一个场地 1i 1 … i 选多少个、 jtot j … t o t 选多少个,这样是 O(n4) O ( n 4 ) 的,根据单调性,我们可以优化成 O(n3) O ( n 3 )


Code

/************************************************
 * Au: Hany01
 * Date: Aug 4th, 2018
 * Prob: BZOJ2436 NOI2011 NOI嘉年华
 * Email: hany01@foxmail.com
 * Inst: Yali High School
************************************************/

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define SZ(a) ((int)(a).size())
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define y1 wozenmezhemecaia

template <typename T> inline bool chkmax(T &a, T b) { return a <= b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }

inline int read() {
    static int _, __; static char c_;
    for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
    for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
    return _ * __;
}

const int maxn = 205;

int n, tot, ls[maxn << 1], f[maxn << 1][maxn], g[maxn << 1][maxn], s[maxn], t[maxn], sum[maxn << 1][maxn << 1], Ans, dp[maxn << 1][maxn << 1];

inline int calc(int i, int j, int l, int r) {
    if (f[i][l] < 0 || g[j][r] < 0) return -INF;
    static int x, y; x = f[i][l] + g[j][r], y = l + r;
    if (x < y) x += sum[i][j]; else y += sum[i][j];
    return min(x, y);
}

int main()
{
#ifdef hany01
    freopen("bzoj2436.in", "r", stdin);
    freopen("bzoj2436.out", "w", stdout);
#endif

    n = read();
    For(i, 1, n) ls[++ tot] = s[i] = read(), ls[++ tot] = t[i] = read() + s[i];
    sort(ls + 1, ls + 1 + tot);
    For(i, 1, n) s[i] = lower_bound(ls + 1, ls + 1 + tot, s[i]) - ls, t[i] = lower_bound(ls + 1, ls + 1 + tot, t[i]) - ls;

    For(i, 1, tot) For(j, i, tot) For(k, 1, n)
        if (i <= s[k] && t[k] <= j) ++ sum[i][j];

    Set(f, 128), Set(g, 128), f[0][0] = 0, g[tot + 1][0] = 0;
    For(i, 1, tot) For(j, 0, n) rep(k, i) {
        chkmax(f[i][j], f[k][j] + sum[k][i]);
        if (j >= sum[k][i]) chkmax(f[i][j], f[k][j - sum[k][i]]);
    }
    For(i, 1, n) chkmax(Ans, min(i, f[tot][i]));
    printf("%d\n", Ans);
    Fordown(i, tot, 1) For(j, 0, n) For(k, i + 1, tot + 1) {
        chkmax(g[i][j], g[k][j] + sum[i][k]);
        if (j >= sum[k][i]) chkmax(g[i][j], g[k][j - sum[i][k]]);
    }

    For(i, 1, tot) For(j, i, tot) if (sum[i][j]) {
        int r = n, now, tmp;
        For(l, 0, n) {
            now = calc(i, j, l, r);
            while (r) if (chkmax(now, calc(i, j, l, r - 1))) -- r; else break;
            chkmax(dp[i][j], now);
        }
    }
    For(i, 1, n) {
        Ans = 0;
        For(l, 1, s[i]) For(r, t[i], tot)
            chkmax(Ans, dp[l][r]);
        printf("%d\n", Ans);
    }

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值