[NOI2009]诗人小G 决策单调性优化DP
第一次写这种二分来优化决策单调性的问题。。。。 调了好久,,,各种细节问题
显然有DP方程:
$f[i]=min(f[j] + qpow(abs(sum[i] - sum[j] - L - 1)));$
其中f[i]代表到了第i个句子的最小答案
qpow用于处理^p
sum为前缀和
(同时为了处理句子之间的空格问题,我们在统计前缀和的时候就默认在句子后面加一个空格,
然后在计算的时候,由于每一行只有最后一个不用加空格,直接减掉这个多加的空格即可获得正确长度)
首先我们可以打表发现是满足决策单调性的,
即决策是一段一段的
比如这种:
11122224446666
但下面这两种则不满足决策单调性:
11144442222666
11112424
同时这样的决策单调性是很特殊的,即一开始可能是这样的
11111111111111
加入2后:
11122222222222
加入4后:
11122224444444
也就是说就算最终取值是11122224446666,
在中间不断插入的过程中,也是一整段的
因此满足可二分性,
所以我们可以二分来找每个决策管理的区间(即可以转移到的区间)
但是有如下细节需要注意:
1,区间可能被完整覆盖
举个栗子,
如果当前状态是
111111111111114
现在加入一个5,那么下一个状态完全有可能是:
111111155555555
因此为了处理这样的情况,我们在二分之前先将会被完整覆盖的区间pop掉
即在插入5前弹出4,
同时这里又要注意,这个pop的处理必须在二分之前,
因为在二分过程中,我们需要一个决策来起到check的作用,而这个决策应该是当前插入的x最后应该被放入的那个区间的决策。
这样说可能不太清楚,还是举个栗子
比如当前状态:
111111112222223333
假设下一个状态是
111111112224444444
那么我们二分过程中作为判断依据的那决策就应该是2,
why?
因为观察到3号决策已经被完整的覆盖了,那么我们要对3之前的状态进行check的时候,由于3号不够优(也有可能是受到只能向后转移的限制),我们不能使用3号来check,
但是直接使用2号决策来check,然后在后面2222223333中二分也是不妥的,
因为在3333时,2号并不是最优,
所以为了应对这种情况,
我们先直接判断3333中的第一个3所对应的DP状态,如果插入的x更加优,
那么就代表这个区间是可以被完整的覆盖的,因此我们pop掉这个区间,
依次pop直到不满足条件为止,
这个时候我们就只需要在222222中二分了,于是用2号决策来check就很顺理成章了
2,新插入的x可能什么区间都覆盖不了,
也就是说新插入的x可能并不能更新状态,于是我们在二分前做一次特判,
判断当前区间的最后一个是否可以被覆盖,
如果不能,那么这个x无法更新状态,
所以直接continue
---------------以上为计算答案过程--------------
----------------以下为输出部分----------------
因为要输出方案,因此我们在转移时用last数组来记录i是从哪个决策转移而来
又因为不能打乱顺序,所以我们用Next数组来反向记录last数组
比如last中记录的可能是
0 <--- 2 <--- 4
即4由2转移而来,2由转移0而来
那么我们Next数组中记录的就是
0 ---> 2 ---> 4
所以我们从1开始输出,
每当我们到达Next[now]时就输出换行
即输出
1 2
3 4
其实也就是相当于存下了每一行是由哪一句话结尾的,
然后依次输出
最后注意ans可能很大,而且要与1e18比较,所以long long 不够用。
要用 long double
下面是代码(中间有调试输出)
其实感觉比斜率优化好理解多了。。。。。QAQ难道是我太蠢了么
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 100100
#define LL long long
#define LD long double
#define ACway 101000
#define inf 1000000000000000000LL
int t,L,p,n;
int s[AC],last[AC],l[AC],r[AC];//对应决策的管理区间
int q[AC],head,tail;//存下当前是哪个决策
int Next[AC];//对last进行相反操作,以便输出
LD f[AC];
LL sum[AC];
char ss[ACway][];
//bool flag;
/*为什么我感觉就是一个普通DP+决策单调性 ---> 二分,,,,这个不比斜率优化容易理解么?*/
inline LD qpow(LD x)//error!!!x也要用LD!!!
{
LD ans=;
// printf("x : %lld\n",(LL)(x + 0.5));
int have=p;
while(have)
{
if(have & ) ans*=x;
x*=x;
have>>=;
// if(ans > inf || x > inf) flag=true;
}
// printf("ans : %lld\n",(LL)(ans + 0.5));
return ans;
} inline void pre()
{
scanf("%d%d%d",&n,&L,&p);
for(R i=;i<=n;i++)
{
scanf("%s",ss[i]+);
s[i]=strlen(ss[i]+) + ;//加上后面的空格
sum[i]=sum[i-] + s[i];//求出前缀和
}
} inline LD count(int x,int j)//j --- > x
{
return f[j] + qpow(abs(sum[x] - sum[j] - L - ));
} void half(int x)//二分查找
{
int now=q[tail],ll=l[now],rr=n,mid;//因为可能可以覆盖多个区间
while(ll < rr)
{
mid=(ll + rr) >> ;
if(count(mid,x) < count(mid,now)) rr=mid;//如果更优就往左缩短
else ll=mid + ;//不然就向右寻找
}
// while(l[q[tail]] >= ll) --tail;//去掉整个都被包含的区间
r[q[tail]]=ll-;
q[++tail]=x,l[x]=ll,r[x]=n;
/*for(R i=head;i<=tail;i++)
{
for(R j=l[q[i]];j<=r[q[i]];j++)
printf("%d",q[i]);
}
cout << endl;*/
} inline void getans()
{
head=,tail=;
q[]=,l[]=,r[]=n;
for(R i=;i<=n;i++)
{
while(r[q[head]] < i) ++head;//如果当前队首已经取不到了
int now=q[head];
//f[i]=f[now] + qpow(abs(sum[i] - sum[now] - L - 1));
f[i]=count(i,now);//error ??? 用函数的话会爆了会自动转换为inf?
//error!!!是后者转移到前者,所以是now ---> i,要填count(i,now),而不是count(now,i);
// printf("???%d\n",now);
last[i]=now;
if(count(n,q[tail]) < count(n,i)) continue;//如果最后一个都不够优,那就不二分了
while(count(l[q[tail]],q[tail]) > count(l[q[tail]],i)) --tail;//如果当前可以覆盖前面的整个区间
half(i);//注意上面的while要在调用half之前修改,这样取到的now才是正确的
}
} inline void write()
{
//if(f[n] > inf || flag) puts("Too hard to arrange");
if(f[n] > inf) puts("Too hard to arrange");
else
{
printf("%lld\n",(LL)(f[n] + 0.5));//注意精度误差
for(R i=n ; i ; i=last[i]) Next[last[i]]=i;
int now=;
for(R i=;i<=n;i++)
{
now=Next[now];//now先跳了吧
int be=now;//先只到这行结尾,因为for还要加的
for(R j=i; j < be ;j++) printf("%s ",ss[j] + );
printf("%s\n",ss[be] + );
i=be;//最后再赋i,因为for中还要用到当前i
}
}
puts("--------------------");
} inline void check()
{
for(R i=;i<=n;i++)
{
if(r[i] > l[i])
for(R j=l[i];j<=r[i];j++) printf("%d",i);
}
printf("\n");
for(R i=;i<=n;i++) printf("!!!%lld\n",(LL)(f[i] + 0.5));
cout << endl << endl;
} inline void work()
{
while(t--)
{
//flag=false;
pre();
getans();
// check();
write();
}
} int main()
{
freopen("in.in","r",stdin);
scanf("%d",&t);
work();
fclose(stdin);
return ;
}
[NOI2009]诗人小G 决策单调性优化DP的更多相关文章
- [BZOJ1563][NOI2009]诗人小G(决策单调性优化DP)
模板题. 每个决策点都有一个作用区间,后来的决策点可能会比先前的优.于是对于每个决策点二分到它会比谁在什么时候更优,得到新的决策点集合与区间. #include<cstdio> #incl ...
- P1912 [NOI2009]诗人小G[决策单调性优化]
地址 n个数划分若干段,给定$L$,$p$,每段代价为$|sum_i-sum_j-1-L|^p$,求总代价最小. 正常的dp决策单调性优化题目.不知道为什么luogu给了个黑题难度.$f[i]$表示最 ...
- bzoj1563: [NOI2009]诗人小G 决策单调性(1D1D)
目录 题目链接 题解 代码 题目链接 bzoj1563: [NOI2009]诗人小G 题解 \(n^2\) 的dp长这样 \(f_i = min(f_j + (sum_i - sum_j - 1 - ...
- BZOJ1563: [NOI2009]诗人小G(决策单调性 前缀和 dp)
题意 题目链接 Sol 很显然的一个dp方程 \(f_i = min(f_j + (sum_i - sum_j - 1 - L)^P)\) 其中\(sum_i = \sum_{j = 1}^i len ...
- BZOJ1563:[NOI2009]诗人小G(决策单调性DP)
Description Input Output 对于每组数据,若最小的不协调度不超过1018,则第一行一个数表示不协调度若最小的不协调度超过1018,则输出"Too hard to arr ...
- [BZOJ 1563] [NOI 2009] 诗人小G(决策单调性)
[BZOJ 1563] [NOI 2009] 诗人小G(决策单调性) 题面 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的.小 G ...
- BZOJ_1563_[NOI2009]诗人小G_决策单调性
BZOJ_1563_[NOI2009]诗人小G_决策单调性 Description Input Output 对于每组数据,若最小的不协调度不超过1018,则第一行一个数表示不协调度若最小的不协调度超 ...
- 2018.09.28 bzoj1563: [NOI2009]诗人小G(决策单调性优化dp)
传送门 决策单调性优化dp板子题. 感觉队列的写法比栈好写. 所谓决策单调性优化就是每次状态转移的决策都是在向前单调递增的. 所以我们用一个记录三元组(l,r,id)(l,r,id)(l,r,id)的 ...
- [NOI2009]诗人小G --- DP + 决策单调性
[NOI2009]诗人小G 题目描述: 小G是一个出色的诗人,经常作诗自娱自乐. 但是,他一直被一件事情所困扰,那就是诗的排版问题. 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并 ...
随机推荐
- NB-IOT修改KV值的方法
1. 安装UEConfigurationEditor-3.22.0.14.msi,之后打开一个fwpkg文件,如下图 2. 找到需要修改的KV值,修改之后,点击Apply changes,应用修改,然 ...
- 阿里otter使用问题汇总
最近在使用otter做为和表从库.(100个分表太难查询了) user_00,user_01...user_99 => user_all 1.问题DDL语句不能执行(exception:setl ...
- MVC、MVVM
一.MVC 所谓的 MVC 是指: Model: 数据的拥有者,实现具体的业务逻辑. View: 具体的用户界面,如按钮.列表.图片. Controller: 负责将 View 中用户的动作传达给 M ...
- tomcat6升级到7时400问题,以及url带有汉字时出错。
tomcat6升级到7时400问题: 在文件catalina.properties后加入tomcat.util.http.parser.HttpParser.requestTargetAllow=|. ...
- 最长回文子串计算(fail)
题意: 给定一个字符串s,在s中找到最长的回文子字符串.您可以假设s的最大长度为1000. 例子: 输入: “babad” 输出: “bab” 注: “aba”也是一个有效的答案. 我的答案: 想法: ...
- Java容器之List接口
List 接口: 1. List 接口是 Collection 的子接口,实现 List 接口的容器类中的元素是有顺序的,而且可以重复: 2. List 容器中的元素都对应一个整数型的序号记载其在容器 ...
- ajax的一些实用技巧
1.尽量优先采用ajax获取html文件,然后再操作dom把数据填充到里面 在实际项目中,如果前端开发人员没有把页面给切分开,那么有如下两种办法可供选择:其一是,在各种点击事件中,用js去拼接并在拼接 ...
- FromHandle临时对象一探究竟
我们在调用CWnd::GetDlgItem()函数时,MSDN告诉我们:The returned pointer may be temporary and should not be stored f ...
- 3ds Max学习日记(七)
第7章讲的是多边形建模,实例略多,有十六个,再加上周日的怠惰感,只做了几个实例. 附上今日的劳动成果: 布料(创建一个平面,转换为可编辑多边形,然后调整顶点,连接一些边,添加网格平滑,转换为可 ...
- 原生js移动端可拖动进度条插件
该插件最初的想法来自网上的一篇文章,直达链接:https://www.cnblogs.com/libin-1/p/6220056.html 笔者因为业务需要寻找到这个插件,然后拿来用之,发现各种不方便 ...