zoukankan      html  css  js  c++  java
  • 【洛谷1973】[NOI2011] NOI 嘉年华(DP)

    点此看题面

    大致题意:(n)个活动,每个活动可以表示为一个区间。你要把活动分给两个嘉年华(可以不分完),使得不会同时在两个嘉年华举行活动(同一嘉年华中没有限制),求两个嘉年华活动数中较小值的最大值。然后对于每个(i),求出在强制选择第(i)个活动时的答案。

    前言

    个人感觉这道题应该还是比较简单的,反正我基本上都能自己推出来。

    自己写出一道(DP)题还是挺有成就感的吧!

    第一问

    这么小的数据范围,第一问应该是非常简单,也非常容易想到的。

    (f_{i,j})为在([1,i])这段时间中,给第一个嘉年华(j)个活动时,第二个嘉年华活动数的最大值。

    考虑如何转移,其实就是枚举(k)作为关键点,并分别讨论把([k+1,i])这段时间中的活动放入哪个嘉年华,得到:((s_{i,j})为完全在([i,j])这段时间中的活动个数)

    [f_{i,j}=max_{k=1}^{i-1}{f_{k,j}+s_{k+1,i},f_{k,j-s_{k+1,i}}} ]

    则第一问的答案显然就是:

    [max_{j=0}^nmin{f_{T,j},j} ]

    然后为了方便处理第二问,我们类似于(f_{i,j})去定义一个(g_{i,j})表示在([i,T])这段时间中,给第一个嘉年华(j)个活动时,第二个嘉年华活动数的最大值。那么转移的过程也类似于(f_{i,j})

    第二问

    容易想到一个最暴力的做法,就是枚举出一个区间([i,j])包含当前活动区间([a_{now},b_{now}]),将这段时间作为完整一段放入一个嘉年华中,得出答案式:

    [max_{i=1}^{a_{now}}max_{j=b_{now}}^Tmax_{x=1}^nmax_{y=1}^nmin{x+y+s_{i,j},f_{i-1,x}+g_{j+1,y}} ]

    这个式子中有(4)重循环,如果还要再枚举(now),就是(5)重循环。但实际上我们可以事先预处理好强制选择每一个([i,j])时的答案(w_{i,j}),时间复杂度就优化为了(O(n^4))

    (nle200)显然就是在刻意卡(O(n^4)),还需要进一步优化。

    考虑随着(x)的增大,(y)的最优决策点一定单调递减,因此只要采用双指针,就可以把时间复杂度优化至(O(n^3))了。

    最终答案为:

    [max_{i=1}^{a_{now}}max_{j=b_{now}}^T w_{i,j} ]

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 200
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    using namespace std;
    int n,a[N+5],b[N+5],dc,dv[2*N+5],s[2*N+5][2*N+5],f[2*N+5][N+5],g[2*N+5][N+5],w[2*N+5][2*N+5];
    int main()
    {
    	RI i,j,k;for(scanf("%d",&n),i=1;i<=n;++i)
    		scanf("%d%d",a+i,b+i),dv[n+i]=(b[i]+=(dv[i]=a[i])-1);//读入区间
    	for(sort(dv+1,dv+2*n+1),dc=unique(dv+1,dv+2*n+1)-dv-1,i=1;i<=n;++i)
    	{
    		a[i]=lower_bound(dv+1,dv+dc+1,a[i])-dv,b[i]=lower_bound(dv+1,dv+dc+1,b[i])-dv;//离散化
    		for(j=a[i];j;--j) for(k=b[i];k<=dc;++k) ++s[j][k];//求出每段区间内活动数
    	}
    	memset(f,-63,sizeof(f)),memset(g,-63,sizeof(g)),f[0][0]=g[dc+1][0]=0;//初始化
    	for(i=1;i<=dc;++i) for(j=s[1][i];~j;--j) for(f[i][j]=f[i][j+1],k=0;k^i;++k)//求f数组
    		Gmax(f[i][j],f[k][j]+s[k+1][i]),j>=s[k+1][i]&&Gmax(f[i][j],f[k][j-s[k+1][i]]);
    	for(i=dc;i>=1;--i) for(j=s[i][dc];~j;--j) for(g[i][j]=g[i][j+1],k=dc+1;k^i;--k)//求g数组
    		Gmax(g[i][j],g[k][j]+s[i][k-1]),j>=s[i][k-1]&&Gmax(g[i][j],g[k][j-s[i][k-1]]);
    	RI t=0;for(i=0;i<=n;++i) Gmax(t,min(f[dc][i],i));printf("%d
    ",t);//求解第一问
    	#define GV(y) min(x+(y)+s[i][j],f[i-1][x]+g[j+1][y])
    	RI x,y;for(i=1;i<=dc;++i) for(j=i;j<=dc;++j)//预处理强制选择区间[i,j]的答案
    		for(x=0,y=n;x<=n;++x) {W(y&&GV(y)<=GV(y-1)) --y;Gmax(w[i][j],GV(y));}//注意最优决策点的单调性
    	for(i=1;i<=n;++i,printf("%d
    ",t))//枚举每一个活动
    		for(t=0,j=1;j<=a[i];++j) for(k=b[i];k<=dc;++k) Gmax(t,w[j][k]);return 0;//求出强制选择该活动时的答案
    }
    
  • 相关阅读:
    ARP攻击原理与解决
    如何查看数据库各种表oracle
    MyEclipse 8.0注册码
    oracle数据库导入导出
    输出设备已满或不可用, 归档程序无法归档重做日志[oracle解决方法]
    句柄以及对象的比较java
    shutdown immediate 后无法启动实例问题解决
    马云经典语录
    海量数据处理分析_BI
    数据库迁移方案
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu1973.html
Copyright © 2011-2022 走看看