[BZOJ1563]詩人小G(1d1d動態規劃)

題目描述 Description

小G是一個出色的詩人,經常作詩自娛自樂。但是,他一直被一件事情所困擾,那就是詩的排版問題。
一首詩包含了若干個句子,對於一些連續的短句,可以將它們用空格隔開並放在一行中,注意一行中可以放的句子數目是沒有限制的。小G給每首詩定義了一個行標準長度(行的長度爲一行中符號的總個數),他希望排版後每行的長度都和行標準長度相差不遠。顯然排版時,不應改變原有的句子順序,並且小G不允許把一個句子分在兩行或者更多的行內。在滿足上面兩個條件的情況下,小G對於排版中的每行定義了一個不協調度, 爲這行的實際長度與行標準長度差值絕對值的P次方,而一個排版的不協調度爲所有行不協調度的總和。
小G最近又作了幾首詩,現在請你對這首詩進行排版,使得排版後的詩儘量協調(即不協調度儘量小),並把排版的結果告訴他。

輸入描述 Input Description

本題中包含多組測試數據。
輸入文件中的第一行爲一個整數T,表示詩的數量。
接下來爲T首詩,這裏一首詩即爲一組測試數據。每組測試數據中的第一行爲三個由空格分隔的正整數N,L,P,其中:N表示這首詩句子的數目,L表示這首詩的行標準長度,P的含義見問題描述。
從第二行開始,每行爲一個句子,句子由英文字母、數字、標點符號等符號組成(ASCII碼33~127,但不包含’-‘)。

輸出描述 Output Description

對於每組測試數據,若最小的不協調度不超過10^18,則第一行爲一個數,表示不協調度。接下來若干行,表示你排版之後的詩。注意:在同一行的相鄰兩個句子之間需要用一個空格分開。
如果有多個可行解,它們的不協調度都是最小值,則輸出任意一個解均可。若最小的不協調度超過10^18,則輸出“Too hard to arrange”(不含引號)。每組測試數據結束後輸出“——————–”(不含引號),共20個“-”,“-”的ASCII碼爲45,請勿輸出多餘的空行或者空格。
由於缺少special judge,因此在這裏只要求輸出最小的不協調度。格式不變,依然以”-“分割。

樣例輸入 Sample Input

4
4 9 3
brysj,
hhrhl.
yqqlm,
gsycl.
4 9 2
brysj,
hhrhl.
yqqlm,
gsycl.
1 1005 6
poet
1 1004 6
poet

樣例輸出 Sample Output

108

32

Too hard to arrange

1000000000000000000

數據範圍及提示 Data Size & Hint

【樣例說明】
前兩組輸入數據中每行的實際長度均爲6,後兩組輸入數據每行的實際長度均爲4。一個排版方案中每行相鄰兩個句子之間的空格也算在這行的長度中(可參見樣例中第二組數據)。每行末尾沒有空格。
這裏寫圖片描述
所有測試點中均滿足句子長度不超過30。

題解

30分算法:
1,2,3組數據:樸素動態規劃,轉移方程:
f[i]=min(f[j]+|s[i]s[j]+ij1L|p)
100分算法:
既然直接dp只有30分,那麼我們該想如何進行優化。
我們可以看出這是一個1D1D動態規劃,那麼決策區間就是連續的段落,於是我們維護一個上凸殼,每次更新的時候用二分就好了,注意也要用隊列來優化。
注意數據比較大,用long double 算完轉long long輸出。

代碼

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

typedef long double ll;
#define inf 9000000000000000000
#define MAX 1000000000000000000LL
using namespace std;
int t,n,l,p,top;
ll sum[100005],f[100005],from[100005];
char ch[100005][35];
struct nod
{
    int l,r,p;
    nod(){}
    nod(int a,int b,int c):l(a),r(b),p(c){}
}q[100005];
inline ll read() {
    ll x=0;
    char c=getchar();
    while(c<'0' || c>'9') c=getchar();
    while(c<='9' && c>='0'){x=x*10+c-'0';c=getchar();}
    return x; 
}
inline ll pow(ll x) {
    if(x<0) x=-x;
    ll ans=1;
    for(int i=1;i<=p;i++) ans*=x;
    return ans;
}
inline ll cal(int j,int i) {
    return f[j]+pow(sum[i]-sum[j]+(i-j-1)-l);
}
int find(nod a,int b) {
    int l=a.l,r=a.r;
    while(l<=r) {
        int mid=l+r>>1;
        if(cal(a.p,mid)<cal(b,mid)) l=mid+1;
        else r=mid-1;
    }
    return l;
}
void dp() {
    int hd=1,tl=0;
    q[++tl]=nod(0,n,0);
    for(int i=1;i<=n;i++) {
        if(hd<=tl && i>q[hd].r) hd++;
        f[i]=cal(q[hd].p,i);from[i]=q[hd].p;
        if(hd>tl || cal(i,n)<=cal(q[tl].p,n)) {
            while(hd<=tl && cal(i,q[tl].l)<=cal(q[tl].p,q[tl].l)) tl--;
            if(hd>tl) q[++tl]=nod(i,n,i);
            else {
                int t=find(q[tl],i);
                q[tl].r=t-1;
                q[++tl]=nod(t,n,i);
            }
        }
    }
}
int main() {
    t=read();
    while(t--) {
        n=read(),l=read(),p=read();
        for(int i=1;i<=n;i++) scanf("%s",ch[i]);
        for(int i=1;i<=n;i++) sum[i]=sum[i-1]+strlen(ch[i]);
        dp();
        if(f[n]>MAX) printf("Too hard to arrange\n");
        else printf("%lld\n",(long long)(f[n]));
        printf("--------------------\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章