• 题解 P1973 【[NOI2011]Noi嘉年华】


    一开始看岔眼了,以为是一道水题,结果发现原来一个会场能同时举办多个活动…
    显然离散化是必须的,毕竟只有相对时间有作用
    然后就可以以时间来作为下标DP了

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

    转移方程很好推:
    f[i][j]=max(f[k][j]+sum[k][i],f[k] [j−sum[k][i]]),k<i,j>=sum[k][i]
    f[i][j]=max(f[k][j]+sum[k][i],f[k] [j−sum[k][i]]),k<i,j>=sum[k][i]

    其中sumsum数组代表从[l,r][l,r]这段时间内的活动总和
    maxmax函数前半部分指的是
    kk时刻A会场举行jj场活动时B会场最多举 行的活动数+kk到ii这段时间内的活动数 也就是将kk到ii这段时间内的活动全在B会场举行
    maxmax函数后半部分同理
    表示,kk时刻A会场举行j−sum[k] [i]j−sum[k][i]场活动时B会场最多举行的活动数
    也就是将kk到ii这段时间内的活动全在A会场举行

    最后的答案就是max(min(j,f[n] [j]))max(min(j,f[n][j]))
    然后题目还要求必定选择某个特定的活动
    其实将原方程变一下
    正取一个ff(pre),倒取一个ff(nxt)
    pre[i][j]pre[i][j]表示从1到ii中A会 场举行jj场活动B会场能举行多少场活动
    nxt[i][j]nxt[i][j]表示从ii到结尾中A会 场举行jj场活动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
    ",ANS[i]);
        return 0;
    }      
     
  • 相关阅读:
    C#笔记(Hex转JPG)
    rpm 和 yum 软件管理
    名称空间和作用域
    网络技术管理和进程管理
    RAID磁盘阵列
    CentOS7系统启动流程:
    磁盘lvm管理
    面向对象 异常处理
    自定义函数和调用函数 return返回值
    Python常用模块
  • 原文地址:https://www.cnblogs.com/Sworddust/p/11427805.html
Copyright © 2020-2023  润新知