[题解] BZOJ 2436 [NOI2011] NOI嘉年华

BZOJ 2436 [NOI2011] NOI嘉年华

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

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

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

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

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

输入描述 Input Description
输入的第一行包含一个整数 n,表示申请的活动个数。
接下来 n 行描述所有活动,其中第 i 行包含两个整数 Si、Ti,表示第 i 个活动从时刻Si开始,持续 Ti的时间。

输出描述 Output Description
输出的第一行包含一个整数,表示在没有任何限制的情况下,活动较少的嘉年华的活动数的最大值。
接下来 n 行每行一个整数,其中第 i 行的整数表示在必须选择第 i 个活动的前提下,活动较少的嘉年华的活动数的最大值。

样例输入 Sample Input
5
8 2
1 5
5 3
3 2
5 3

样例输出 Sample Output
2
2
1
2
2
2

数据范围及提示 Data Size & Hint
对于10%的数据, 1 <= n <= 10
对于30%的数据, 1 <= n <= 40
对于100%的数据, 1 <= n <= 200 , 0 <= S i <= 10 9 , 1 <= T i <= 10 9

Solution

一开始看岔眼了,以为是一道傻*题(详见YBT活动选择),结果发现原来一个会场能同时举办多个活动…

扫描二维码关注公众号,回复: 1425888 查看本文章

显然离散化是必须的,毕竟只有相对时间有作用
然后就可以以时间来作为下标DP了

获取全局最优解比较简单, f [ i ] [ j ] 表示到 i 时刻A会场举行 j 场活动时B会场最多进行多少活动

转移方程很好推:

f [ i ] [ j ] = m a x ( f [ k ] [ j ] + s u m [ k ] [ i ] , f [ k ] [ j s u m [ k ] [ i ] ] ) , k < i , j >= s u m [ k ] [ i ]

其中 s u m 数组代表从 [ l , r ] 这段时间内的活动总和
m a x 函数前半部分指的是
k 时刻A会场举行 j 场活动时B会场最多举行的活动数+ k i 这段时间内的活动数
也就是将 k i 这段时间内的活动全在B会场举行

m a x 函数后半部分同理
表示, k 时刻A会场举行 j s u m [ k ] [ i ] 场活动时B会场最多举行的活动数
也就是将 k i 这段时间内的活动全在A会场举行

最后的答案就是 m a x ( m i n ( j , f [ n ] [ j ] ) )

然后题目还要求必定选择某个特定的活动
其实将原方程变一下
正取一个 f (pre),倒取一个 f (nxt)
p r e [ i ] [ j ] 表示从1到 i 中A会场举行 j 场活动B会场能举行多少场活动
n x t [ i ] [ j ] 表示从 i 到结尾中A会场举行 j 场活动B会场能举行多少场活动
然后枚举中间部分(num),再加上左右区间就好了

为什么程序中貌似直接将num[i][j]给了B会场呢?其实是因为A,B会场是等价的…
可以试试看将num[i][j]直接给A会场,也是能AC的

具体看代码吧

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N=205;
const int INF=1e9;
int b[N],e[N];
int s[N<<1];
int pre[N<<1][N],nxt[N<<1][N];
int num[N<<1][N<<1];
int mus[N<<1][N<<1];
int ANS[N];
int n;
int read() {
    int ans=0,flag=1;
    char ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    if(ch=='-') { flag=-1;ch=getchar(); }
    while(ch>='0' && ch<='9') { ans=ans*10+ch-'0';ch=getchar(); }
    return ans*flag;
}
int main() {
    n=read();
    for(int i=1;i<=n;++i) {
        b[i]=read();
        e[i]=b[i]+read();
        s[i*2-1]=b[i];
        s[i*2]=e[i];
    }
    sort(s+1,s+n*2+1);
    s[0]=unique(s+1,s+n*2+1)-(s+1);
    for(int i=1;i<=n;++i) {
        b[i]=lower_bound(s+1,s+s[0]+1,b[i])-s;
        e[i]=lower_bound(s+1,s+s[0]+1,e[i])-s;
    }
    for(int i=1;i<=s[0];++i)
        for(int j=i;j<=s[0];++j)
            for(int k=1;k<=n;++k)
                if(b[k]>=i && e[k]<=j)
                    ++num[i][j];
    for(int t=1;t<=s[0];++t)
        for(int i=1;i<=n;++i)
            pre[t][i]=nxt[t][i]=-1;
    pre[0][0]=nxt[s[0]+1][0]=0;
    for(int t=1;t<=s[0];++t) {
        for(int i=0;i<=num[1][t];++i) pre[t][i]=max(pre[t-1][i],pre[t][i]);//继承上面答案
        for(int i=0;i<=num[1][t];++i)
            for(int j=1;j<t;++j) {
                if(i>=num[j][t])
                    pre[t][i]=max(pre[j][i-num[j][t]],pre[t][i]);//num[j][t]都由1会场拿到
                if(pre[j][i]!=-1)
                    pre[t][i]=max(pre[j][i]+num[j][t],pre[t][i]);//num[j][t]都由2会场拿到
            }
    }
    for(int t=s[0];t;--t) {
        for(int i=0;i<=num[t][s[0]];++i) nxt[t][i]=max(nxt[t+1][i],nxt[t][i]);//继承上面答案
        for(int i=0;i<=num[t][s[0]];++i)
            for(int j=s[0];j>t;--j) {
                if(i>=num[t][j])
                    nxt[t][i]=max(nxt[j][i-num[t][j]],nxt[t][i]);//num[t][j]都由1会场拿到
                if(nxt[j][i]!=-1)
                    nxt[t][i]=max(nxt[j][i]+num[t][j],nxt[t][i]);//num[t][j]都由2会场拿到
            }
    }
    for(int i=1;i<=s[0];++i)
        for(int j=i+1;j<=s[0];++j) {
            for(int x=0,y=n;x<=n;++x) {
                if(pre[i][x]==-1) continue;
                while(y) {
                    int val1=min(x+y,pre[i][x]+num[i][j]+nxt[j][y]);
                    int val2=min(x+y-1,pre[i][x]+num[i][j]+nxt[j][y-1]);
                    if(val2>=val1 || nxt[j][y]==-1) --y;
                    else break;
                }
                mus[i][j]=max(mus[i][j],min(x+y,pre[i][x]+num[i][j]+nxt[j][y]));//必选num[i][j]到2会场的答案
                //也可以改成mus[i][j]=max(mus[i][j],min(x+y+num[i][j],pre[i][x]+nxt[j][y]));
                //当然,如果你要改这一句的话上面val1,val2也需要改
            }
        }
    for(int i=0;i<=num[1][s[0]];++i)
        ANS[0]=max(ANS[0],min(pre[s[0]][i],i));
    for(int i=1;i<=n;++i)
        for(int l=b[i];l>=1;--l)
            for(int r=e[i];r<=s[0];++r)
                ANS[i]=max(ANS[i],mus[l][r]);
    for(int i=0;i<=n;i++) printf("%d\n",ANS[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/force_chl/article/details/80462231