题解——[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难道是我太蠢了么

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 100100
  5 #define LL long long
  6 #define LD long double
  7 #define ACway 101000
  8 #define inf 1000000000000000000LL
  9 int t,L,p,n;
 10 int s[AC],last[AC],l[AC],r[AC];//对应决策的管理区间
 11 int q[AC],head,tail;//存下当前是哪个决策
 12 int Next[AC];//对last进行相反操作,以便输出
 13 LD f[AC];
 14 LL sum[AC];
 15 char ss[ACway][45];
 16 //bool flag;
 17 /*为什么我感觉就是一个普通DP+决策单调性 ---> 二分,,,,这个不比斜率优化容易理解么?*/
 18 inline LD qpow(LD x)//error!!!x也要用LD!!!
 19 {
 20     LD ans=1;
 21 //  printf("x : %lld\n",(LL)(x + 0.5));
 22     int have=p;
 23     while(have)
 24     {
 25         if(have & 1) ans*=x;
 26         x*=x;
 27         have>>=1;
 28     //  if(ans > inf || x > inf) flag=true;
 29     }
 30 //  printf("ans : %lld\n",(LL)(ans + 0.5));
 31     return ans;
 32 }
 33
 34 inline void pre()
 35 {
 36     scanf("%d%d%d",&n,&L,&p);
 37     for(R i=1;i<=n;i++)
 38     {
 39         scanf("%s",ss[i]+1);
 40         s[i]=strlen(ss[i]+1) + 1;//加上后面的空格
 41         sum[i]=sum[i-1] + s[i];//求出前缀和
 42     }
 43 }
 44
 45 inline LD count(int x,int j)//j --- > x
 46 {
 47     return f[j] + qpow(abs(sum[x] - sum[j] - L - 1));
 48 }
 49
 50 void half(int x)//二分查找
 51 {
 52     int now=q[tail],ll=l[now],rr=n,mid;//因为可能可以覆盖多个区间
 53     while(ll < rr)
 54     {
 55         mid=(ll + rr) >> 1;
 56         if(count(mid,x) < count(mid,now)) rr=mid;//如果更优就往左缩短
 57         else ll=mid + 1;//不然就向右寻找
 58     }
 59 //  while(l[q[tail]] >= ll) --tail;//去掉整个都被包含的区间
 60     r[q[tail]]=ll-1;
 61     q[++tail]=x,l[x]=ll,r[x]=n;
 62     /*for(R i=head;i<=tail;i++)
 63     {
 64         for(R j=l[q[i]];j<=r[q[i]];j++)
 65             printf("%d",q[i]);
 66     }
 67     cout << endl;*/
 68 }
 69
 70 inline void getans()
 71 {
 72     head=1,tail=1;
 73     q[1]=0,l[0]=1,r[0]=n;
 74     for(R i=1;i<=n;i++)
 75     {
 76         while(r[q[head]] < i) ++head;//如果当前队首已经取不到了
 77         int now=q[head];
 78         //f[i]=f[now] + qpow(abs(sum[i] - sum[now] - L - 1));
 79         f[i]=count(i,now);//error ??? 用函数的话会爆了会自动转换为inf?
 80         //error!!!是后者转移到前者,所以是now ---> i,要填count(i,now),而不是count(now,i);
 81     //  printf("???%d\n",now);
 82         last[i]=now;
 83         if(count(n,q[tail]) < count(n,i)) continue;//如果最后一个都不够优,那就不二分了
 84         while(count(l[q[tail]],q[tail]) > count(l[q[tail]],i)) --tail;//如果当前可以覆盖前面的整个区间
 85         half(i);//注意上面的while要在调用half之前修改,这样取到的now才是正确的
 86     }
 87 }
 88
 89 inline void write()
 90 {
 91     //if(f[n] > inf || flag) puts("Too hard to arrange");
 92     if(f[n] > inf) puts("Too hard to arrange");
 93     else
 94     {
 95         printf("%lld\n",(LL)(f[n] + 0.5));//注意精度误差
 96         for(R i=n ; i ; i=last[i])  Next[last[i]]=i;
 97         int now=0;
 98         for(R i=1;i<=n;i++)
 99         {
100             now=Next[now];//now先跳了吧
101             int be=now;//先只到这行结尾,因为for还要加的
102             for(R j=i; j < be ;j++) printf("%s ",ss[j] + 1);
103             printf("%s\n",ss[be] + 1);
104             i=be;//最后再赋i,因为for中还要用到当前i
105         }
106     }
107     puts("--------------------");
108 }
109
110 inline void check()
111 {
112     for(R i=1;i<=n;i++)
113     {
114         if(r[i] > l[i])
115             for(R j=l[i];j<=r[i];j++) printf("%d",i);
116     }
117     printf("\n");
118     for(R i=1;i<=n;i++) printf("!!!%lld\n",(LL)(f[i] + 0.5));
119     cout << endl << endl;
120 }
121
122 inline void work()
123 {
124     while(t--)
125     {
126         //flag=false;
127         pre();
128         getans();
129     //  check();
130         write();
131     }
132 }
133
134 int main()
135 {
136     freopen("in.in","r",stdin);
137     scanf("%d",&t);
138     work();
139     fclose(stdin);
140     return 0;
141 }

原文地址:https://www.cnblogs.com/ww3113306/p/8954768.html

时间: 2024-10-19 13:16:38

题解——[NOI2009]诗人小G 决策单调性优化DP的相关文章

BZOJ 1563 诗人小G(四边形优化)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1563 题意: 思路:设s[i]表示前i个句子的长度和,那么有转移方程: 有大牛证明这个满足四边形不等式,证明应该 比较复杂.在<1D/1D动态规划优化初步>这篇文章中,作者说实战中可以直接打表看看是不是满足,感觉还是比较实用的.不管那么多了,现在我们知道了满 足四边形不等式,也就是满足决策点单调.比如f[i]是用j更新的,那么i之后的点不可能用j之前的点更新,这就是决策单调性.那么我

决策单调性优化dp 专题练习

决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队列 : 在保证插入和查询的x坐标均具有单调性时可以使用 2.单调栈+二分:保证插入有单调性,不保证查询有单调性 3.分治+ 1 或 2:在每次分治时将\([l,mid]\)这段区间排序后插入,然后更新右区间\([mid+1,r]\)的答案 二.分治.单调队列维护有单调性的转移 (甚至还有分治套分治)

Bzoj 1563: [NOI2009]诗人小G(决策单调性优化)

原题面 带有详细证明的转这里 题意:每一个线段有一个长度,有一个标准长,现在要把这些线段按照顺序分行,每行的不和谐值等于标准长和该行线段总长的差的绝对值的p次方.现在要求最小的不和谐值之和. 开始的时候完全读错题了,以为p==2 for ever.真是太天真.后来看数据范围才发现.我真是面向数据编程? n^2的dp是一眼秒的.然后如果是p=2,那就是一个非常像玩具装箱的斜率优化dp.对于4.5的数据范围.可以想到用贪心优化dp.因为每一行的长度不会通过拼接线段(线段条数>=2)达到2*标准长,这

决策单调性优化dp

决策单调性: 对于一些dp方程,经过一系列的猜想和证明,可以得出,所有取的最优解的转移点(即决策点)位置是单调递增的. 即:假设f[i]=min(f[j]+b[j]) (j<i) 并且,对于任意f[i]的决策点g[i],总有f[i+1]的决策点g[i+1]>=g[i](或者<=g[i]) 那么,这个方程就具备决策单调性. 这个有什么用吗? 不懂具体优化方法的话确实也没有什么用.可能还是n^2的.只不过范围可能少了一些. 经典入门例题: Description: [POI2011]Ligh

Codeforces 868F. Yet Another Minimization Problem【决策单调性优化DP】【分治】【莫队】

LINK 题目大意 给你一个序列分成k段 每一段的代价是满足\((a_i=a_j)\)的无序数对\((i,j)\)的个数 求最小的代价 思路 首先有一个暴力dp的思路是\(dp_{i,k}=min(dp_{j,k}+calc(j+1,i))\) 然后看看怎么优化 证明一下这个DP的决策单调性: trz说可以冥想一下是对的就可以 所以我就不证了 (其实就是决策点向左移动一定不会更优) 然后就分治记录当前的处理区间和决策区间就可以啦 //Author: dream_maker #include<bi

BZOJ 1563 NOI2009 诗人小G 四边形不等式

题目大意:玩具装箱,然而指数变成了p(p≤10) 首先我们需要证明决策单调 由于数死早,还是戳这里吧 知道决策单调之后怎么办呢? 由于是1D1D,所以不能分治了 每个决策点能决策的区间一定是连续的一段 并且随着决策点的右移 这个区间也在不断右移 令g[j]表示决策点j能贡献的最左侧的位置 然后我们开一个栈来维护当前存在贡献的贡献点 那么显然stack[i]的贡献区间是[g[stack[i]],g[stack[i+1]]?1] 每新来一个点,首先在栈中二分找到最优决策点 然后将所有满足 f[sta

【BZOJ 1563】 [NOI2009]诗人小G

Description Input Output 对于每组数据,若最小的不协调度不超过1018,则第一行一个数表示不协调度若最小的不协调度超过1018,则输出"Too hard to arrange"(不包含引号).每个输出后面加"--------------------" Sample Input 4 4 9 3 brysj, hhrhl. yqqlm, gsycl. 4 9 2 brysj, hhrhl. yqqlm, gsycl. 1 1005 6 poet

[BZOJ4709][JSOI2011]柠檬 决策单调性优化dp

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4709 我好弱啊QAQ,网上dalao们的题解根本看不懂啊,折腾了几个小时,有一点明白了. 首先要把朴素dp方程退出来. ①题目中说每次从序列的左右选一端取,但是如果你真的照着题目说的这样做我也不知道会怎么样.事实上很明显不管怎么取,最终答案都只跟划分出的是哪几个区间有关.所以不妨从左端开始取. ②如果取一个区间,区间第一个贝壳的大小和最后一个贝壳的大小不一样,那么很明显可以去掉第一个或最

NOI 2009A 诗人小G

NOI 2009A 诗人小G 诗人小G [问题描述] 小G是一个出色的诗人,经常作诗自娱自乐.但是,他一直被一件事情所困扰,那就是诗的排版问题. 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中, 注意一行中可以放的句子数目是没有限制的.小G给每首诗定义了一个行标准长度(行的长度为一行中符号的总个数),他希望排版后每行的长度都和行标准长度相差不远.显然排版时,不应改变原有的句子顺序,并且小G不允许把一个句子分在两行或者更多的行内.在满足上面两个条件的情况下,小G对于排