BZOJ 1563: [NOI2009]詩人小G 決策單調性DP

題目大意:給定若干個字符串,可以將相鄰的若干個字符串連接起來並在其中插入空格,最小化每個字符串與給定長度的差的絕對值的p次方。
題解:一眼看上去像是之前做過的斜率優化,但是仔細一看不是平方變成了p次方,這就是斜率優化做不了的了,查了題解才知道這是決策單調性DP,決策單調性DP就是假如有如下方程:F[i]=max(f[j]+W(i,j))其中W(i,j)是有關i,j的式子,若能證明W(i+1,j)+W(i,j+1)>= W(i,j)+ W(i+1,j+1) 則有對於任意i<=j 一定有i的決策點小於等於j的決策點,實際做題的時候不推薦證明,可以先寫出個暴力來看決策點單調行,反正最後也要對拍233,知道了這個性質以後我們就可以用一個棧並二分找到每個狀態能夠更新的狀態從哪開始,從而在nlogn的時間複雜度內解決問題。我們現在來看這道題,我們可以很方便的列出DP方程:F[i]=max(F[j]+|sum[j]-sum[i]+j-i+1-L|^P)至於爲什麼滿足四邊形不等式這裏就不證明了(我不會233,那麼接下來就是模板時間了233。因爲數字太大,所以我們用double存,快速冪的時候要寫成“慢速冪”,否則會GG。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
typedef long double ld;
long long limit=1000000000000000000ll;
ld ksm(ld x,int t)
{
    long double re=1;
    for(int i=1;i<=t;i++)
        re*=x;
    return re;
}
ld sum[200000];
ld f[200000];
int stack[200000];
int top;
int g[200000];
int P,n,L;
ld F(int i,int j)
{
    return f[j]+ksm(fabs(sum[i]-sum[j]+(i-j-1)-L),P);   
}
int get_pos(int x)
{
    int l=1,r=top;
    int jilu=-1;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(g[stack[mid]]<=x) jilu=mid,l=mid+1;
        else r=mid-1;
    }
    return stack[jilu];
}
int erfen(int k)
{
    int l=max(g[stack[top]]-1,k)+1;
    int r=n;
    int j=stack[top];
    int jilu=n+1;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(F(mid,j)>F(mid,k)) jilu=mid,r=mid-1;
        else l=mid+1;
    }
    return jilu;
}
char s[50];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&L,&P);
        for(int i=1;i<=n;i++) 
        {
            scanf("%s",s+1);
            sum[i]=sum[i-1]+strlen(s+1);
        }
        f[0]=0;
        g[0]=1;
        stack[top=1]=0;
        for(int i=1;i<=n;i++)
        {
            int pos=get_pos(i);
            f[i]=F(i,pos);
            while(i<g[stack[top]] && F(g[stack[top]],stack[top])>F(g[stack[top]],i)) stack[top--]=0;
            pos=erfen(i);
            if(pos!=n+1)
            {
                stack[++top]=i;
                g[i]=pos;
            }
        }
        if(f[n]-0.5>limit) puts("Too hard to arrange"),puts("--------------------");
        else printf("%lld\n--------------------\n",(long long) (f[n]+0.5)); 
    }
    return 0;
}
發佈了123 篇原創文章 · 獲贊 5 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章