決策單調性總結

決策單調性,通常用於1d/1d。
就是說,對於任意\(a<b<c<d\),若滿足在c處轉移到b比a優,那麼在d處也滿足。
另外一種理解:轉移決策點單調不降。
證明:主要靠打表。
得到這個性質後,我們有兩種方法:

1、分治法
每次取分治區間的中點,並暴力在可行區間內找到它的最優轉移位置。
之後,根據這個位置,分治兩邊的位置,並縮小可行區間的範圍。
時間複雜度:\(O(nlogn)\)
優點:好寫,好理解,並且找轉移位置是連續尋找的,有的問題會比較方便。
缺點:要求dp之間沒有依賴(即沒有計算順序的要求),比如多階段dp。

2、二分+雙端隊列法
我們按照順序進行dp,並維護每個位置當前狀態下的最優轉移位置。
當計算到\(i\)時:
首先,算出\(dp(i)\)
其次,算出用它轉移的區間。根據定義,只要找到第一個用\(i\)轉移比當前更優的位置,那麼之後的都是由它轉移更優。
這個位置可以使用二分查找。
因此,我們需要實現後綴覆蓋,\(O(1)\)單點查詢。似乎不容易。
由於後綴覆蓋具有均攤性,我們考慮維護轉移位置相同的若干段。
維護一個隊列,每個節點記錄它對應的區間,和轉移位置。
在二分查找時,我們從隊尾反向遍歷,並在這個區間內二分查找,若找不到,則退出。否則,把這個從隊尾扔出。
若沒有完全覆蓋,則把這個區間縮小。最後,在隊尾加入當前區間。

注意:
1、在依次計算dp時,要把隊首沒用的彈出。
2、在二分查找時,左端點要和\(i+1\)\(\max\)
3、要特判\(i\)不能更新任何位置的情況。

優點:適用情況更普遍,比如詩人小G
缺點:細節較多,難以理解,相對難寫。

剛纔那題的代碼:

#include <stdio.h>
#include <string.h>
#include <math.h>
#define max(a,b) a>b?a:b
#define ld long double
ld dp[100010];
ld ksm(ld a,int b)
{
    ld jg=1;
    while(b>0)
    {
        if(b&1)
            jg*=a;
        a*=a;b=(b>>1);
    }
    return jg;
}
char zf[100010][31];
int he[100010],wz[100010],st[100010],l,p;
ld cal(int i,int j)
{
    return dp[j]+ksm(fabs((he[i]-he[j]-1)-l),p);
}
struct SJd
{
    int l,r,z;
    SJd(){}
    SJd(int L,int R,int Z)
    {
        l=L;r=R;z=Z;
    }
};
SJd dl[200010];
int efcz(int l,int r,int x,int y)
{
    while(l<r)
    {
        int m=(l+r)>>1;
        if(cal(m,x)<cal(m,y))
            r=m;
        else
            l=m+1;
    }
    return l;
}
int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&l,&p);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",zf[i]);
            he[i]=strlen(zf[i])+he[i-1]+1;
        }
        dl[0]=SJd(1,n,0);
        for(int i=1,he=0,ta=1;i<=n;i++)
        {
            while(he<ta&&i>dl[he].r)
                he+=1;
            int j=dl[he].z,zd=false;
            dp[i]=cal(i,j);wz[i]=j;
            while(he<ta&&efcz(max(dl[ta-1].l,i+1),dl[ta-1].r+1,i,dl[ta-1].z)<=dl[ta-1].r)
                ta-=1,zd=true;
            if(!zd)continue;
            int l=efcz(max(dl[ta].l,i+1),dl[ta].r,i,dl[ta].z);
            if(l>dl[ta].l)
                dl[ta++].r=l-1;
            dl[ta++]=SJd(l,n,i);
        }
        if(dp[n]>1e18)
            printf("Too hard to arrange\n");
        else
        {
            printf("%.0Lf\n",dp[n]);
            int u=n,m=0;
            while(u>0)
            {
                st[m++]=u;
                u=wz[u];
            }
            for(int i=m-1,la=0;i>=0;i--)
            {
                for(int j=la+1;j<=st[i];j++)
                {
                    printf("%s",zf[j]);
                    if(j<st[i])printf(" ");
                }
                la=st[i];
                printf("\n");
            }
        }
        printf("--------------------");
        if(T>0)printf("\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章