1D1D动态规划优化

1D1D动态规划优化

1D/1D 动态规划优化初步
所谓1D/1D 动态规划,指的是状态数为O(n),每一个状态决策量为O(n)的动态规划方程。
直接求解的时间复杂度为O(n2),但是,绝大多数这样的方程通过合理的组织与优化都是可
以优化到O(nlogn)乃至O(n)的时间复杂度的。这里就想讲一讲我对一些比较初步的经典的
优化方法的认识。
本文中不想进行过多的证明与推导,主要想说明经典模型的建立、转化与求解方法。
由于本人认识与水平相当有限,如果出现什么错误与疏漏,还请大牛多多指正。另外,也
希望大牛们更多地向我们介绍一下有关动态规划优化的更深入的东西。
本文中使用两种方式表示一个函数:f(x)与f[x],用方括号表示的函数值可以在规划之前
全部算出(常量),而用圆括号表示的函数值必须在规划过程中计算得到(变量)。无论是什
么函数值一经确定,在以后的计算中就不会更改。

我们总是沿着“f(x)的最优决策是什么”这个思路进行思考,下面我们换一个角度,
思考对于一个已经计算出来的状态f(j),“f(j)能够更新的状态有哪些”。这样,每一步过程中
某些状态的决策可能不是最优的,但是当算法结束的时候所有状态对应的决策一定是最优
的。
一开始,只有f(1)的函数值被计算出来,于是所有状态的当前最优决策都是1。
111111111111111111111111111111111111111111111111111111111111111
现在,显然f(2)的值已经确定了:它的最有决策只能是1。我们用决策2 来更新这个决策
表。由于决策单调性,我们知道新的决策表只能有这样的形式:

111111111111111111111111111111222222222222222222222222222222
这意味着我们可以使用二分法来查找“转折点”,因为如果在一个点x 上,如果决策2 更
好,则所有比x 大的状态都是决策2 更好;如果x 上决策1 更好,则所有比x 小的状态都是
决策1 更好。
现在决策1 和决策2 都已经更新完毕,则f(3)业已确定,现在用决策3 来更新所有状态。
根据决策单调性,现在的决策表只能有以下2 种类型:
11111111111111111111111111111111122222222222222222233333333333
1111111111111111111111111333333333333333333333333333333333333
而这样的决策表示绝对不会出现的:
111111111111333333333333333333322222222222222222222222222222,不可能。
那么,我们的更新算法就是:
1、 考察决策2 的区间[b,e]的b 点上是否决策3 更优,如果是,则全部抛弃决策2,将此
区间划归决策3;如果否,则在决策2 的区间[b,e]中二分查找转折点。
2、 如果第1 问的回答是“是”,则用同样的方法考察决策1。
推演到这一步,相信决策单调性的实现算法已经明了了:使用一个栈来维护数据,占中的
每一个元素保存一个决策的起始位置与终了位置,显然这些位置相互连接且依次递增。当插
入一个新的决策时,从后到前扫描栈,对于每一个老决策来说,做这样两件事:
1、 如果在老决策的起点处还是新决策更好,则退栈,全额抛弃老决策,将其区间合并至
新决策中,继续扫描下一个决策。
2、 如果在老决策的起点处是老决策好,则转折点必然在这个老决策的区间中;二分查找
之,然后新决策进栈,结束。
由于一个决策出栈之后再也不会进入,所以均摊时间为O(1),但是由于二分查找的存在,
所以整个算法的时间复杂度为O(nlogn)。

noi2009a 诗人小G对应的1D1D动态规划优化分析

  1 //不要去洛谷上面提交,洛谷的这题数据有问题,去codeVs
  2 #include<cstdio>
  3 #include<cstring>
  4 #define ll long double
  5 //node[i]代表i,里面的l代表决策的作用起点,r代表决策的作用终点,p是决策的值
  6 struct node{int  l,r,p;}q[100100];
  7 #define MAX 1000000000000000000LL
  8 #define N 100100
  9 ll sum[N],f[N];
 10 int n,l,p,T;
 11 char ch[35];
 12 //求|y|^p的函数
 13 ll pow(ll y){
 14     if(y<0)y=-y;
 15     ll ans=1;
 16     for (int i=1;i<=p;i++) ans*=y;
 17     return ans;
 18 }
 19 //计算不协调度
 20 ll calc(int x,int y){
 21     return f[x]+pow(sum[y]-sum[x]+(y-x-1)-l);
 22 }
 23 //二分查找找决策转折点
 24 //node表示老决策点,x表示新决策点的值
 25 //这个函数就是在老的决策点的范围中寻找新的决策点的值
 26 int find(node t,int x){
 27     int l=t.l,r=t.r;
 28     while(l<=r){//当头小于等于尾,也就是还有数可以查找的时候
 29         //mid=(头+尾)/2
 30         int mid=(l+r)>>1;
 31         //小于的情况,也就是新的决策点更有,我们就要一直往前继续扩展新的决策点的起点,
 32         //直到老决策点比较好的时候
 33         if (calc(x,mid)<=calc(t.p,mid)) r=mid-1;
 34         //大于的情况,也就老决策点比较好的时候,我们往后搜索新决策点的作用域的起点
 35         else l=mid+1;
 36     }
 37     //返回头
 38     return l;
 39 }
 40
 41 void dp(){
 42     //用数组模拟栈,头置为1,尾置为0
 43     int head=1,tail=0;
 44     //将第一个决策入栈,决策1的起点为0,终点为n,决策的初始值置为0,因为初始决策就是0
 45     q[++tail]=(node){0,n,0};
 46     //求取f[i]
 47     for (int i=1;i<=n;i++){
 48         //如果栈头结点上的决策作用终点小于i,说明这个决策已经无法对i进行作用,所以我们要换新的决策
 49         //head<=tail栈的头小于等于尾,说明栈里面还有新的决策,那就换上新的决策
 50         //因为head++,所以此时头节点是指向新的决策
 51         if(q[head].r<i&&head<=tail) head++;
 52         //用新的可以作用i位置的决策来计算f[i]的值,这样计算出来的f[i]的值就是最优值
 53         f[i]=calc(q[head].p,i);
 54         //calc(i,n)表示通过值为i的决策去计算f[n]的值
 55         //calc(q[tail].p,n)表示通过旧决策q[tail].p去计算f[n]的值
 56         //calc(i,n)<=calc(q[tail].p,n)表示值为i的决策优于老决策,我们才进行下一步
 57         //不然,如果老决策更好,我们根部用不着换决策
 58
 59         //每一个被计算出来的f[i],都会使i成为新的决策,因为我有了f[i]的值,
 60         //所以我可以用f[i]的值帮助计算别的f[k](k>i),所以f[i]就是新的决策
 61
 62         /*
 63         比如说我们的样例1中,当i==3时,我们通过 q[head].p=2这个决策把f[3]算出来了
 64         比如说这里的n是4,如果calc(3,4)<=calc(2,4),那说明值为3的这个新决策更好,
 65         我们就要在决策2的作用区间里面找出决策3的作用区间
 66         */
 67
 68
 69         //如果新决策更好,找出新决策的作用范围
 70         //确定新决策点的终点
 71         if (calc(i,n)<=calc(q[tail].p,n)){
 72             //如果栈的头小于等于尾,head<=tail,说明栈里有元素
 73             //calc(q[tail].p,q[tail].l)表示用旧决策点决策值来决策旧决策点的起点
 74             //calc(i,q[tail].l)用新决策点来决策旧决策点的起点
 75             //如果后者小于前者,说明新决更好,那就要在更广阔的范围里面去寻找新决策点的决策范围
 76             //换句话说,就是当新决策点的作用范围覆盖了老决策点,我们要到比旧决策点更老点决策点里面寻找新决策点的作用方位
 77             while(head<=tail&&calc(q[tail].p,q[tail].l)>=calc(i,q[tail].l)) tail--;
 78             //head>tail说明栈已经为空了,就是说现在新决策点最好,所以把新决策点入栈
 79             if(head>tail)q[++tail]=(node){i,n,i};
 80             else{
 81                 //否则,我们就要去寻找新决策点的作用范围
 82                 //x返回新决策点范围的起点
 83                 int x=find(q[tail],i);
 84                 //旧决策点的尾部置为x-1
 85                 q[tail].r=x-1;
 86                 //将新决策点入栈
 87                 q[++tail]=(node){x,n,i};
 88             }
 89         }
 90     }
 91 }
 92
 93 int main(){
 94     scanf("%d",&T);
 95     while(T--){
 96         scanf("%d%d%d",&n,&l,&p);
 97         for (int i=1;i<=n;i++) scanf("%s",ch),sum[i]=sum[i-1]+strlen(ch);
 98         dp();
 99         if(f[n]>MAX)
100             puts("Too hard to arrange");
101         else
102             printf("%lld\n",(long long)f[n]);
103         puts("--------------------");
104     }
105     return 0;
106 }
时间: 2024-11-08 13:07:00

1D1D动态规划优化的相关文章

【bzoj2216】[Poi2011]Lightning Conductor 1D1D动态规划优化

Description 已知一个长度为n的序列a1,a2,…,an.对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p – sqrt(abs(i-j)) Input 第一行n,(1<=n<=500000)下面每行一个整数,其中第i行是ai.(0<=ai<=1000000000) Output n行,第i行表示对于i,得到的p Sample Input 6532424 Sample Output 235354 题解 http

POJ 3046 Ant Counting(“动态规划” 优化递推关系式)

http://poj.org/problem?id=3046 蚂蚁牙黑,蚂蚁牙红:有A只蚂蚁,来自T个家族.同一个家族的蚂蚁长得一样,但是不同家族的蚂蚁牙齿颜色不同.任取n只蚂蚁(S<=n<=B),求能组成几种集合? 这是<2.3 记录结果再利用的"动态规划" 优化递推关系式>练习题的第二题. 定义 dp[i][j] := 使用前i个家族可以配出来"元素个数为j"的集合的个数. 那么dp[0][0] = 1,不使用任何蚂蚁配出空集的个数为1.

诗人小G(1D1D动态规划)

1D1D动态规划的转移式是长这样的: 对于形如f(i)=min(f(j)+w(j,i)) ,1<=i<=n-1f(i)=min(f(j)+w(j,i)),1<=i<=n?1的状态转移方程,记p[i]p[i]为令f[i]f[i]取到最小值的jj的值,即p[i]p[i]是f[i]f[i]的最优决策.若pp在[1,N][1,N]上单调不见,则称f具有决策单调性 其中 w[i,x]需要满足四边形不等式 emmm四边形不等式是什么呢? 对于定义域上的任意整数a,b,c,d,其中a\le b

nyoj546 Divideing Jewels (动态规划+优化||搜索+剪枝)

题意:给你n个宝珠,然后给宝珠个价值,价值范围[1,10],能不能均分给两个人. 输入的数据:价值分别为1,2,3...10的有多少个. 这道题和队友做了好久最终在结束前5分钟AC..第一次简单的动规结果TLE 不过和队友认为一定是动规.就在想如何优化,想到如果某个价值的个数有偶数个就不判断,奇数个就存入数组. 结果还是wr...我们不放弃啊...为就想着写几组数组,当0 2 0 1 0 0 0 0 0 0 输出结果为不能,显然答案是能的. 还剩下不到20分钟了.这个时候就是靠感觉了,直接就写如

动态规划优化

bzoj2064 分裂 存在通解:把原始集合都合并,再一一拆开. 如果可以划分一些集合,使得原始集合和目标集合对应的小集合相等,那么可以节省操作次数. ans=(n1-1)+(n2-1)-2*(x-1) x为划分的相同集合数. n<=10,状压 另外,其实原始集合一个x,就是往右x步,目标集合一个y,就是往左y步. 那么,两个个集合对应和相等,就是走出去再回来. f[S1][S2]表示,原始集合选择S1的元素,目标集合选择S2元素,最多可以凑成几个和相等的集合.也即能走多少个圈. 预处理集合对应

队列优化和斜率优化的dp

可以用队列优化或斜率优化的dp这一类的问题为 1D/1D一类问题 即状态数是O(n),决策数也是O(n) 单调队列优化 我们来看这样一个问题:一个含有n项的数列(n<=2000000),求出每一项前面的第m个数到它这个区间内的最小值 可以使用RMQ求区间最小值,那么时间复杂度是O(nlogn),不是让人很满意. dp[i]为i-m+1->i这个区间的最小值. 那么状态转移方程是 可以看出,这个题目的状态数是O(n),决策数是O(m),且决策的区间是连续的,那么可以尝试想办法把O(m)优化成O(

HNOI2008玩具装箱toy题解

题目描述 Description P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具,第i件玩具经过压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的.同时如果一个一维容器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一个容器中,那么容器的长度将为x=j?i+Sigmajk=

bzoj1563

P<=10一开始是吓死我了 后来想到这就是一个经典的决策单调性解决1d1d动态规划的题目 像决策单调性完全可以打表找规律,这里有一篇严谨的证明https://www.byvoid.com/blog/noi-2009-poet 关于1d1d动归的优化可以看<1d1d动态规划优化初步> 注意可能会爆longlong,所以用extended计算 1 type node=record 2 l,r,x:longint; 3 end; 4 5 var q:array[0..100010] of no

2017 济南集训DAY1.AF

LIST T1 水题(water) T2 梦境(dream) T2 动态规划(dp) T1 水题(water) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK出了道水题. 这个水题是这样的:有两副牌,每副牌都有n张. 对于第一副牌的每张牌长和宽分别是xi和yi.对于第二副牌的每张牌长和宽分别是aj和bj.第一副牌的第i张牌能覆盖第二副牌的第j张牌当且仅当xi>=aj并且yi>=bj.(注意牌不能翻转)当然一张牌只能去覆盖最多一张牌,而不能覆盖好多