luogu P1973 [NOI2011]NOI 嘉年华 dp
LINK:NOI 嘉年华
一道质量非常高的dp题目。
考虑如何求出第一问 容易想到dp.
按照左端点排序/右端点排序状态还是很难描述。
但是我们知道在时间上肯定是一次选一段 所以就可以直接利用时间点来dp.
直接将时间离散再做 因为有两个元素使得最大的最小 二分是无意义的。
每次选择是一段所以没必要知道上次选到的时间点在哪 直接枚举上次决策。
分别记录两个地方的值是否存在也是可以优化的 可以只记录一个然后第二个变成最多拿到多少即可。
这样就有状态\(f_{i,j}\)表示到了i这个时间点第一个的值为j的时候第二个的最大值。
转移枚举k 然后预处理\(c_{i,j}\)表示这段时间的任务数即可。
复杂度\(n^3\)
考虑第二问。强制选择某个点 在dp中是很难做到的 所以可以考虑强行将某个区间给第一个会场然后求出第二个会场的最大值。
强行给的话这个地方可以使用前后包夹做 即再做一个后缀dp g数组。
那么强行包含\(l,r\)的最大dp值就是 \(ans_{l,r}=\sum_{x}\sum_{y}max(x+y+c_{l,r},f_{l,x}+g_{r,y})\)
求出这个东西看似是\(n^4\)的 实际上随着x的增大y是不增的。
证明这一点可以进行比较繁杂的分类讨论 不过可以比较直观的想出来。这里不再赘述。
所以可以优化到\(n^3\).
考虑最后的答案 对于一个询问l,r 答案不一定是\(l,r\) 因为此时是把l r单独给抽出来了。
可能一些大的区间一起并且包含其可能是最优的 所以此时枚举包含这个区间的一些区间取max即可。
总复杂度\(n^3\).
code
//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define gc(a) scanf("%s",a+1)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define vep(p,n,i) for(RE int i=p;i<n;++i)
#define pii pair<int,int>
#define mk make_pair
#define RE register
#define P 1000000007
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-4
#define sq sqrt
#define S second
#define F first
#define mod 1000000007
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
RE int x=0,f=1;RE char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
const int MAXN=410;
int n,m,cnt;
int b[MAXN];
int f[MAXN][MAXN],g[MAXN][MAXN],c[MAXN][MAXN],ans[MAXN][MAXN];
struct wy{int l,r;}t[MAXN];
int main()
{
//freopen("1.in","r",stdin);
get(n);
rep(1,n,i)
{
int get(l),get(r)+l;
t[i]=(wy){l,r};
b[++cnt]=l;b[++cnt]=r;
}
sort(b+1,b+1+cnt);
rep(1,cnt,i)if(i==1||b[i]!=b[i-1])b[++m]=b[i];
rep(1,n,i)
{
t[i].l=lower_bound(b+1,b+1+m,t[i].l)-b;
t[i].r=lower_bound(b+1,b+1+m,t[i].r)-b;
rep(1,t[i].l,j)rep(t[i].r,m,k)++c[j][k];
//cout<<t[i].l<<' '<<t[i].r<<endl;
}
memset(f,0xcf,sizeof(f));
memset(g,0xcf,sizeof(g));
f[1][0]=0;g[m][0]=0;
rep(2,m,i)rep(0,c[1][i],j)
{
rep(1,i-1,k)
{
f[i][j]=max(f[i][j],f[k][j]+c[k][i]);
if(j>=c[k][i])f[i][j]=max(f[i][j],f[k][j-c[k][i]]);
}
//cout<<i<<' '<<j<<' '<<f[i][j]<<endl;
}
fep(m-1,1,i)rep(0,c[i][m],j)rep(i+1,m,k)
{
g[i][j]=max(g[i][j],g[k][j]+c[i][k]);
if(j>=c[i][k])g[i][j]=max(g[i][j],g[k][j-c[i][k]]);
}
rep(1,m,i)
{
rep(i,m,j)
{
int y=n;
rep(0,n,x)
{
while(y&&min(c[i][j]+x+y,f[i][x]+g[j][y])<=min(c[i][j]+x+y-1,f[i][x]+g[j][y-1]))--y;
ans[i][j]=max(ans[i][j],min(c[i][j]+x+y,f[i][x]+g[j][y]));
}
//cout<<i<<' '<<j<<' '<<ans[i][j]<<endl;
}
}
int ww=0;
rep(1,n,i)ww=max(ww,min(i,f[m][i]));
put(ww);
rep(1,n,i)
{
ww=0;
rep(1,t[i].l,j)rep(t[i].r,m,k)ww=max(ww,ans[j][k]);
put(ww);
}
return 0;
}
luogu P1973 [NOI2011]NOI 嘉年华 dp的更多相关文章
- 洛谷P1973 [NOI2011]Noi嘉年华(动态规划,决策单调性)
洛谷题目传送门 DP题怕是都要大大的脑洞...... 首先,时间那么大没用,直接离散化. 第一问还好.根据题意容易发现,当一堆活动的时间有大量重叠的时候,更好的办法是把它们全部安排到一边去.那么我们转 ...
- 洛谷P1973 [NOI2011]Noi嘉年华(决策单调性)
传送门 鉴于FlashHu大佬讲的这么好(而且我根本不会)我就不再讲一遍了->传送 //minamoto #include<iostream> #include<cstdio& ...
- cogs 1377. [NOI2011] NOI嘉年华 (dp
题意:给你n个活动的起止时间,要你从中选一些活动在2个会场安排(不能有两个活动在两个会场同时进行),使活动较少的会场活动数最大,以及在某个活动必须选择的前提下,求该答案. 思路:由于n很小,时间很大, ...
- P1973 [NOI2011]Noi嘉年华
传送门 首先可以把时间区间离散化 然后求出 $cnt[l][r]$ 表示完全在时间 $[l,r]$ 之内的活动数量 设 $f[i][j]$ 表示当前考虑到时间 $i$,第一个会场活动数量为 $j$ 时 ...
- 【BZOJ 2436】 2436: [Noi2011]Noi嘉年华 (区间DP)
2436: [Noi2011]Noi嘉年华 Description NOI2011 在吉林大学开始啦!为了迎接来自全国各地最优秀的信息学选手,吉林大学决定举办两场盛大的 NOI 嘉年华活动,分在两个不 ...
- 2436: [Noi2011]Noi嘉年华 - BZOJ
Description NOI2011 在吉林大学开始啦!为了迎接来自全国各地最优秀的信息学选手,吉林大学决定举办两场盛大的 NOI 嘉年华活动,分在两个不同的地点举办.每个嘉年华可能包含很多个活动, ...
- bzoj 2436: [Noi2011]Noi嘉年华
Description NOI2011 在吉林大学开始啦!为了迎接来自全国各地最优秀的信息学选手,吉林大学决定举办两场盛大的 NOI 嘉年华活动,分在两个不同的地点举办.每个嘉年华可能包含很多个活动, ...
- BZOJ2436 [Noi2011]Noi嘉年华 【dp】
题目链接 BZOJ2436 题解 看这\(O(n^3)\)的数据范围,可以想到区间\(dp\) 发现同一个会场的活动可以重叠,所以暴力求出\(num[l][r]\)表示离散化后\([l,r]\)的完整 ...
- bzoj2436: [Noi2011]Noi嘉年华
我震惊了,我好菜,我是不是该退役(苦逼) 可以先看看代码里的注释 首先我们先考虑一下第一问好了真做起来也就这个能想想了 那么离散化时间是肯定的,看一手范围猜出是二维DP,那对于两个会场,一个放自变量, ...
随机推荐
- BJDCTF-2020-WRITEUP---TiKi小组
title: BJDCTF 2020 刷题记录categories: CTFtags: BJDCTF CTF2020 BJDCTF Web duangShell 根据提示,输入.index.php.s ...
- PE文件格式详解(六)
0x00 前言 前面两篇讲到了输出表的内容以及涉及如何在hexWorkShop中找到输出表及输入DLL,感觉有几个地方还是没有理解好,比如由数据目录表DataDirectory[16]找到输出表表后以 ...
- URL编码转换函数:escape()、encodeURI()、encodeURIComponent()讲解
转自:https://www.cnblogs.com/douJiangYouTiao888/p/6473874.html 函数出现时间: escape() ...
- 深入Mybatis源码——执行流程
前言 上一篇分析Mybatis是如何加载解析XML文件的,本篇紧接上文,分析Mybatis的剩余两个阶段:代理封装和SQL执行. 正文 代理封装 Mybatis有两种方式调用Mapper接口: pri ...
- 2018年BRATS 肿瘤分割挑战赛第三名分割方案One-pass Multi-task Networks with Cross-task Guided Attention for Brain Tumor Segmentation
首先说一下我对这个方案的看法,相比第一名与第二名的方案,这个方案的分割方法确实复杂的多,原论文是发表在MICCAI,后来砖投到IEEE image processing(SCI 1区),总体感觉给人一 ...
- day06总结
字符串常用操作# ======================================基本使用======================================# 1.用途:记录描述 ...
- 数据可视化之DAX篇(三) 认识DAX中的表函数和值函数
https://zhuanlan.zhihu.com/p/64421003 学习 DAX 的过程中,会遇到各种坑,刚开始甚至无法写出一个正确的度量值,总是提示错误.其实很多原因都是不理解 DAX 函数 ...
- Python面向对象01 /面向对象初识、面向对象结构、类、self、实例化对象
Python面向对象01 /面向对象初识.面向对象结构.类.self.实例化对象 目录 Python面向对象01 /面向对象初识.面向对象结构.类.self.实例化对象 1. 面向对象初识 2. 面向 ...
- hihoCoder 1049 后序遍历 最详细的解题报告
题目来源:后序遍历 解题思路:开始时我只知道先通过先序.中序求出二叉树,然后再后序遍历二叉树,这当然也是一种解题思路,但是会做一些无用功,比如:计算二叉树.其实,可以直接通过先序序列和中序序列直接求出 ...
- SonarQube+Jenkins+Cppcheck实现C++代码扫描
背景:公司部分项目是由C++进行开发,因此对此有需求. sonarqube:docker化安装(alpine系统),版本8.3.1 (build 34397) jenkins:docker化安装,版本 ...