诗人小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 \le c \le da≤b≤c≤d
都有w(a,d)+w(b,c)\ge w(a,c)+w(b,d)w(a,d)+w(b,c)≥w(a,c)+w(b,d)成立,则称函数w满足四边形不等式
(另一种定义)
对于定义域上的任意整数a,b,其中a<ba<b
都有w(a,b+1)+w(a+1,b)\ge w(a,b)+w(a+1,b+1)w(a,b+1)+w(a+1,b)≥w(a,b)+w(a+1,b+1)成立,则称函数w满足四边形不等式

至于证明(我才高一我不会很正常)
然后就可以进行1D1D动态规划啦!
1D1D的本质(我理解的本质)就是现将所有决策点的决策值定为1 然后以此加入2决策值,3决策值……每次加入决策值都可以通过二分来找决策点 通过判断当前二分的决策点是采用新加入的决策值更优还是之前的方案更优
关于栈。。其实我觉得这种更像是(单调)队列 因为我们总是保证q[head]是最优解
在实现时我们使用一个qq结构体,存储l,r,numl,r,num,表示num决策点的起点与终点是l,r
然后我们进行以下操作
1.判断当前q[head].r<iq[head].r<i 如果是的话,就弹出队首,因为当前决策点i至少都是从位置i开始进行决策的,所以当前队首对当前决策点没有影响,后面也没有影响,应该弹出。
2.判断当前的calc(i,n)<=calc(q[tail].p,n)calc(i,n)<=calc(q[tail].p,n) 如果是的话,表明当前的决策比队尾要优秀,应当判断当前决策点的范围以及终点
3.如果calc(q[tail].p,q[tail].l)>=calc(i,q[tail].l)calc(q[tail].p,q[tail].l)>=calc(i,q[tail].l) 表明当前决策点比队尾优秀,队尾弹出
4.否则说明决策点在当前队尾的范围内,需要通过二分查找
5.加入新的决策点i (真是不容易啊)
那么终于我可以上代码啦

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <cmath>
#define ld long double
#define int long long
using namespace std;
const int maxn=500010;
const ld MAX=1e18;

struct node{
    int l;int r;int num;
}q[200010];
int n,m,p,len,pr[maxn];
ld sum[maxn];
char ch[maxn][50];
ld f[maxn];

ld pow(ld b){
    if(b<=0)     b=-b;
    ld a=1;
    for(int i=1;i<=p;i++)
        a*=b;
    return a;
}

ld calc(int x,int y)
{
    return f[x]+pow(sum[y]-sum[x]+y-x-1-m);//此处y-x-1是因为如果两句话合并在一行,会有空格 需要占据一个长度
}

int find(node t,int x)
{
    int l=t.l,r=t.r;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if (calc(x,mid)<=calc(t.num,mid)) r=mid-1;
        else l=mid+1;
    }
    return l;
}

void dp()
{
    int head=1,tail=0;
    q[++tail]=(node){0,n,0};
    for(int i=1;i<=n;i++)//i代表的当前的决策为i
    {
        if(q[head].r<i && head<=tail)//这里如果队头的右端点小于i,因为这个状态对当前状态没有影响
            head++;
        f[i]=calc(q[head].num,i);
        pr[i]=q[head].num;
        if(calc(i,n)<=calc(q[tail].num,n))
        {
            while(head<=tail&&calc(q[tail].num,q[tail].l)>=calc(i,q[tail].l)) tail--;//对于f[lt]来说,i比jt更优
            // 注意此处是与tail的左端点进行比较,因为左端点才能决定是否更优
            //如果新决策点更优 则说明队尾全部都不如新决策点
            //如果新决策点在左端点不是更优,说明了新决策点的左端点在tail的l,r之内
            if(head>tail)q[++tail]=(node){i,n,i}; //如果队列空了,说明当前决策点优于所有队列中的点,直接入队
                else{
                int x=find(q[tail],i);
                q[tail].r=x-1;
                q[++tail]=(node){x,n,i};
            }
        }
    }
}

signed main()
{
    int t;scanf("%lld",&t);
    while(t--)
    {
        cin >> n >> m >> p;

        for(int i=1;i<=n;i++)
        {
            scanf("%s",ch[i]);
            len=strlen(ch[i]);
            sum[i]=sum[i-1]+len;
        }
        dp();
        if(f[(int)n]>MAX)
                {
            puts("Too hard to arrange");
            printf("--------------------\n");
        }
                else
                {
                    printf("%lld\n",(long long)f[(int)n]);
                    //printf("%.0f\n",(double)f[(int)n]);//long double转成double 输出
                    int t,i;
                    for(q[t=0].num=i=n;i;q[++t].num=i=pr[i]);
                    for(;t;--t){
                        for(i=q[t].num+1;i<q[t-1].num;++i)
                            printf("%s ",ch[i]);
                        puts(ch[i]);
                        }printf("--------------------\n");
                }
    }
}

总结,1D1D动态规划是一种非常好的算法,而且如果找出了这道题的dp式,并且满足决策点单调递增的,并且w(j,i)w(j,i)满足四边形不等式 那么我们就可以愉快的使用1D1D啦!
(悄悄说一句,基本上所有的1D1D动态规划的打法都是一样的,只有calccalc函数不太一样)
还有一个附加知识点,就是longlong doubledouble函数的输出可以转换为long long 形式或者double形式。
(又悄悄说一句,这道题和玩具装箱一摸一样,这道题的空格相当于玩具之间的填装物,句子的长度相当于玩具的长度,还有常数也是相同的意义。所以唯一的区别就是一个需要输出方案,一个不需要输出方案)

原文地址:https://www.cnblogs.com/mendessy/p/11651387.html

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

诗人小G(1D1D动态规划)的相关文章

NOI 2009A 诗人小G

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

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

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

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(决策单调性优化)

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

【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

题解——[NOI2009]诗人小G 决策单调性优化DP

第一次写这种二分来优化决策单调性的问题.... 调了好久,,,各种细节问题 显然有DP方程: f[i]=min(f[j] + qpow(abs(sum[i] - sum[j] - L - 1))); 其中f[i]代表到了第i个句子的最小答案 qpow用于处理^p sum为前缀和 (同时为了处理句子之间的空格问题,我们在统计前缀和的时候就默认在句子后面加一个空格, 然后在计算的时候,由于每一行只有最后一个不用加空格,直接减掉这个多加的空格即可获得正确长度) 首先我们可以打表发现是满足决策单调性的,

1D1D动态规划优化

1D1D动态规划优化 1D/1D 动态规划优化初步所谓1D/1D 动态规划,指的是状态数为O(n),每一个状态决策量为O(n)的动态规划方程.直接求解的时间复杂度为O(n2),但是,绝大多数这样的方程通过合理的组织与优化都是可以优化到O(nlogn)乃至O(n)的时间复杂度的.这里就想讲一讲我对一些比较初步的经典的优化方法的认识.本文中不想进行过多的证明与推导,主要想说明经典模型的建立.转化与求解方法.由于本人认识与水平相当有限,如果出现什么错误与疏漏,还请大牛多多指正.另外,也希望大牛们更多地

ACM_小G的循环

小G的循环 Time Limit: 2000/1000ms (Java/Others) Problem Description: 一回生,二回熟,三回就腻,小G用for,while循环用得太多了,累觉不爱.于是自定义了一个循环,叫circle,形式如下: void circle(int n) { n--; if(n == 0) return; else circle(n); } 已知循环次数为该函数的调用次数,现在输入n(-2^31< =n< =2^31-1),求circle()的循环次数.

ACM_开挂的小G

开挂的小G Time Limit: 2000/1000ms (Java/Others) Problem Description: 小G寒假在家没网络,闲着没事干又看不下书,就玩起了单机游戏ACM_Game,不过,学计算机的怎么能老玩别人的游戏呢?自己做个?哦不,自己做的要是玩得不开心就会随便改代码啦,这样就没意思了.于是,小G费了点心思就给自己做了个ACM_Game辅助,让自己角色的分数快速增长.有多快呢?程序运行后,第一秒增加1,第2秒增加4,第3秒增加9... 第n秒就增加n^2.小G知道,