zoukankan      html  css  js  c++  java
  • 题解 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;
    }      
     
  • 相关阅读:
    洛谷 P1226 【模板】快速幂||取余运算 题解
    洛谷 P2678 跳石头 题解
    洛谷 P2615 神奇的幻方 题解
    洛谷 P1083 借教室 题解
    洛谷 P1076 寻宝 题解
    洛谷 UVA10298 Power Strings 题解
    洛谷 P3375 【模板】KMP字符串匹配 题解
    Kafka Shell基本命令
    Mybatis与Hibernate的详细对比
    MyBatis简介
  • 原文地址:https://www.cnblogs.com/Sworddust/p/11427805.html
Copyright © 2011-2022 走看看