LOJ#2087 国王饮水记

解:这个题一脸不可做...
比1小的怎么办啊,好像没用,扔了吧。
先看部分分,n = 2简单,我会分类讨论!n = 4简单,我会搜索!n = 10,我会剪枝!
k = 1怎么办,好像选的那些越大越好啊,那么我就排序之后枚举后缀!
k = INF怎么办啊,好像最优策略是从小到大一个一个连通啊,那直接模拟好了。
写一写,有40分了。
别的怎么办啊,拿搜索找规律吧?于是发现一个规律(伪):最优策略一定是单独选择最后k - 1个,和前面的一个后缀。
于是枚举后缀,然后模拟后面的部分,成功得到了61分!
#include <bits/stdc++.h> // ---------- decimal lib start ---------- // ---------- decimal lib end ---------- const int N = ; int n, k, p;
Decimal a[N]; inline void output(Decimal x) {
std::cout << x.to_string(p + ) << std::endl;
return;
} inline void output(double x) {
printf("%.10f\n", x);
return;
} inline void out(int x) {
for(int i = ; i < n; i++) {
printf("%d", (x >> i) & );
}
return;
} namespace bf { const double eps = 1e-; double ans = , a[N], temp[][];
int lm, pw[], Ans[N], now[N]; void DFS(int x) {
/// check
double large();
for(int i = ; i <= n; i++) {
if(large < a[i]) large = a[i];
}
if(large < ans + eps) {
return;
}
if(fabs(large - a[]) < eps || x > k) {
if(ans < a[]) {
ans = a[];
memcpy(Ans + , now + , x * sizeof(int));
}
return;
} for(int i = ; i <= n; i++) {
temp[x - ][i] = a[i];
}
for(int s = lm - ; s > ; s--) {
if(!(s - (s & (-s)))) continue;
double tot = ;
int cnt = ;
int t = s;
while(t) {
int tt = pw[t & (-t)] + ;
tot += a[tt];
cnt++;
t ^= << (tt - );
}
tot /= cnt;
t = s;
while(t) {
int tt = pw[t & (-t)] + ;
a[tt] = tot;
t ^= << (tt - );
}
now[x] = s;
DFS(x + );
t = s;
while(t) {
int tt = pw[t & (-t)] + ;
a[tt] = temp[x - ][tt];
t ^= << (tt - );
}
}
now[x] = ;
return;
} inline void solve() { /// DFS
lm = << n;
for(int i = ; i < n; i++) {
pw[ << i] = i;
}
for(int i = ; i <= n; i++) {
a[i] = ::a[i].to_double();
}
DFS(); output(ans); /*for(int i = 1; i <= k + 1; i++) {
out(Ans[i]); printf(" ");
}
puts("");*/
return;
}
} int main() { //freopen("in.in", "r", stdin); scanf("%d%d%d", &n, &k, &p);
for(int i = , x; i <= n; i++) {
scanf("%d", &x);
a[i] = x;
} std::sort(a + , a + n + );
if(n == ) {
output(a[]);
return ;
}
if(n == ) {
if(a[] > a[]) {
output(a[]);
}
else {
a[] = (a[] + a[]) / ;
output(a[]);
}
return ;
}
if(a[] >= a[n]) {
output(a[]);
return ;
}
if(k == ) {
Decimal tot = a[], ans = a[];
int cnt = ;
for(int i = n; i >= ; i--) {
cnt++;
tot += a[i];
ans = std::max(ans, tot / cnt);
}
output(ans);
return ;
}
if(k >= n - ) {
for(int i = ; i <= n; i++) {
if(a[] > a[i]) continue;
a[] = (a[] + a[i]) / ;
}
output(a[]);
return ;
}
if(n <= ) {
bf::solve();
return ;
}
else {
Decimal tot = a[], ans = a[];
int cnt = ;
for(int i = n - k + ; i >= ; i--) {
cnt++;
tot += a[i];
ans = std::max(ans, tot / cnt);
}
a[] = ans;
for(int i = n - k + ; i <= n; i++) {
if(a[] > a[i]) continue;
a[] = (a[] + a[i]) / ;
}
output(a[]);
return ;
}
return ;
}
61分代码
正确的规律:最优策略一定是把一个后缀分成连续若干段。
于是以此DP,设f[i][j]表示前i次操作取到了j,此时1号点的最大值。转移就枚举从哪来即可。注意初始化。
#include <bits/stdc++.h> // ---------- decimal lib start ---------- const int PREC = ; // ---------- decimal lib end ---------- const int N = ; int n, k, p;
Decimal a[N]; inline void output(Decimal x) {
std::cout << x.to_string(p + ) << std::endl;
return;
} inline void output(double x) {
printf("%.10f\n", x);
return;
} inline void out(int x) {
for(int i = ; i < n; i++) {
printf("%d", (x >> i) & );
}
return;
} namespace bf { const double eps = 1e-; double ans = , a[N], temp[][];
int lm, pw[], Ans[N], now[N]; void DFS(int x) {
/// check
double large();
for(int i = ; i <= n; i++) {
if(large < a[i]) large = a[i];
}
if(large < ans + eps) {
return;
}
if(fabs(large - a[]) < eps || x > k) {
if(ans < a[]) {
ans = a[];
memcpy(Ans + , now + , x * sizeof(int));
}
return;
} for(int i = ; i <= n; i++) {
temp[x - ][i] = a[i];
}
for(int s = lm - ; s > ; s--) {
if(!(s - (s & (-s)))) continue;
double tot = ;
int cnt = ;
int t = s;
while(t) {
int tt = pw[t & (-t)] + ;
tot += a[tt];
cnt++;
t ^= << (tt - );
}
tot /= cnt;
t = s;
while(t) {
int tt = pw[t & (-t)] + ;
a[tt] = tot;
t ^= << (tt - );
}
now[x] = s;
DFS(x + );
t = s;
while(t) {
int tt = pw[t & (-t)] + ;
a[tt] = temp[x - ][tt];
t ^= << (tt - );
}
}
now[x] = ;
return;
} inline void solve() { /// DFS
lm = << n;
for(int i = ; i < n; i++) {
pw[ << i] = i;
}
for(int i = ; i <= n; i++) {
a[i] = ::a[i].to_double();
}
DFS(); output(ans); /*for(int i = 1; i <= k + 1; i++) {
out(Ans[i]); printf(" ");
}
puts("");*/
return;
}
} Decimal f[][];
int sum[N]; inline void solve() {
int I = ;
while(a[] > a[I]) ++I;
for(int i = ; i <= n; i++) {
f[][i] = a[];
}
for(int i = ; i <= k; i++) {
f[i][I - ] = a[];
for(int j = I; j <= n; j++) {
/// f[i][j]
f[i][j] = f[i - ][j];
for(int p = I - ; p < j; p++) {
Decimal t = (f[i - ][p] + sum[j] - sum[p]) / (j - p + );
if(f[i][j] < t) {
f[i][j] = t;
}
}
//printf("i = %d j = %d f = ", i, j); output(f[i][j]);
}
}
output(f[k][n]);
return;
} int main() { //freopen("in.in", "r", stdin);
//printf("%d \n", (sizeof(f)) / 1048576); scanf("%d%d%d", &n, &k, &p);
for(int i = , x; i <= n; i++) {
scanf("%d", &x);
a[i] = x;
} std::sort(a + , a + n + );
for(int i = ; i <= n; i++) {
sum[i] = sum[i - ] + (int)(a[i].to_double() + 0.5);
}
if(n == ) {
output(a[]);
return ;
}
if(n == ) {
if(a[] > a[]) {
output(a[]);
}
else {
a[] = (a[] + a[]) / ;
output(a[]);
}
return ;
}
if(a[] >= a[n]) {
output(a[]);
return ;
}
if(k == ) {
Decimal tot = a[], ans = a[];
int cnt = ;
for(int i = n; i >= ; i--) {
cnt++;
tot += a[i];
ans = std::max(ans, tot / cnt);
}
output(ans);
return ;
}
if(k >= n - ) {
for(int i = ; i <= n; i++) {
if(a[] > a[i]) continue;
a[] = (a[] + a[i]) / ;
}
output(a[]);
return ;
}
if(n <= ) {
bf::solve();
return ;
}
else {
solve();
return ;
}
return ;
}
60分代码
考虑如何优化这个DP。
LOJ#2087 国王饮水记的更多相关文章
- 【BZOJ4654】【NOI2016】国王饮水记(动态规划,斜率优化)
[BZOJ4654][NOI2016]国王饮水记(动态规划,斜率优化) 题面 BZOJ 洛谷 题解 首先肯定是找性质. 明确一点,比\(h_1\)小的没有任何意义. 所以我们按照\(h\)排序,那么\ ...
- [UOJ#223][BZOJ4654][Noi2016]国王饮水记
[UOJ#223][BZOJ4654][Noi2016]国王饮水记 试题描述 跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中.跳蚤国最大的问题就是饮水问题,由于首都中居住的跳 ...
- luogu P1721 [NOI2016]国王饮水记 斜率优化dp 贪心 决策单调性
LINK:国王饮水记 看起来很不可做的样子. 但实际上还是需要先考虑贪心. 当k==1的时候 只有一次操作机会.显然可以把那些比第一个位置小的都给扔掉. 然后可以得知剩下序列中的最大值一定会被选择. ...
- [Noi2016]国王饮水记
来自FallDream的博客,未经允许,请勿转载,谢谢. 跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中.跳蚤国最大的问题就是饮水问题,由于首都中居住的跳蚤实在太多,跳蚤国王 ...
- BZOJ4654/UOJ223 [Noi2016]国王饮水记
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- uoj233/BZOJ4654/洛谷P1721 [Noi2016]国王饮水记 【dp + 斜率优化】
题目链接 uoj233 题解 下面不加证明地给出几个性质: 小于\(h[1]\)的城市一定是没用的 任何城市联通包含\(1\)且只和\(1\)联通一次 联通顺序从小到大最优 单个联通比多个一起联通要优 ...
- [NOI 2016]国王饮水记
Description 题库链接 给出 \(n\) 个水杯,每个水杯装有不同高度的水 \(h_i\) ,每次可以指定任意多水杯用连通器连通后断开,问不超过 \(k\) 次操作之后 \(1\) 号水杯的 ...
- *UOJ#223. 【NOI2016】国王饮水记
$n \leq 8000$的数列,问不超过$m \leq 1e9$次操作后第一个数字最大是多少.操作:选一些数,把他们变成他们的平均值.需要保留$p \leq 3000$位小数,提供了一个小数高精度库 ...
- BZOJ4654 NOI2016国王饮水记(动态规划+三分)
有很多比较显然的性质.首先每个城市(除1外)至多被连通一次,否则没有意义.其次将城市按水位从大到小排序后,用以连通的城市集合是一段前缀,并且不应存在比1城市还小的.然后如果确定了选取的城市集合,每次选 ...
随机推荐
- 【转】JavaSE面试题目收集
1.是否可以从一个static方法内部发出对非static方法的调用? 不可以.当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中发出对非static方法的调用, ...
- QT中使用google breakpad捕获程序崩溃异常
今天给大家介绍一个在linux下如何捕获程序崩溃异常的方法 一.google breakpad源码的下载和编译 1.https://github.com/google/breakpad.git,源码地 ...
- 测者的测试技术手册:Junit单元测试遇见的一个枚举类型的坑(枚举类型详解)
Enum的简介 枚举类型很早就在计算机语言中存在了,主要被用来将一组相似的值包含进一种类型中,这种类型的名称被定义成独一无二的类型描述符,这就是枚举类型. 在java语言中,枚举类型是一个完整功能的类 ...
- Jenkins 配置 Git 错误解决:CAfile: C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
错误信息: Failed to connect to repository : Command "C:/tools/Git/bin/git.exe ls-remote -h https:/X ...
- 删除Widows 启动项中的信息
1.打开任务管理器切换到启动Tab,在需要删除的项目上点击右键,点击打开文件所在位置,这样就找到了启动项所在磁盘位置,可以根据需要决定是否删除. 2.从注册表中删除在启动中的注册信息. regedit ...
- Python基础之字典
字典初识 字典的认识 首先,我们来认识一下字典: # 定义一个字典 user_dict = {"name": "zhangsan", "age&quo ...
- Jenkins-2.154 windows平台部署 FAQ
部署过程中遇到的问题及解决办法如下 1.如何将 Jenkins 汉化? 1.进入系统管理 -> 插件管理 -> 选中“可选插件” 标签 -> 在过滤条件中输入“local”进行查找插 ...
- day22 面向对象
面向对象 ''''1.面向过程编程 核心是"过程"二字,过程指的是解决问题的步骤,即先干什么再干什么 基于该思想编写程序就好比在编写一条流水线,是一种机械式的思维方式 ...
- Redis详解(五)------ redis的五大数据类型实现原理
前面两篇博客,第一篇介绍了五大数据类型的基本用法,第二篇介绍了Redis底层的六种数据结构.在Redis中,并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这些对 ...
- SequoiaDB 巨杉数据库
传统单点数据库的容量瓶颈,仅仅是分布式数据库所解决的问题之一.更重要的是在未来微服务化应用开发以及云化平台的趋势下,应用不再以“烟囱式”的中间件加数据库模式进行构建,而是采用数千甚至上万的微服务程序构 ...