NOI2009诗人小G(P=2时做法)

    技术2022-05-20  42

    此题 P=2 的情况和 APIO2009 的特别行动队差不多,也可以推出凸单调性

    P=2 时题意简述】

    一首初始为 N 行的诗,每行包括一个句子。你可以把连续的若干个句子放在一行,但同一行的句子之间必须有一个空格。定义排版后每行的不整齐度为 (Len-L)^2 Len 表示该行的长度(包括句子之间的空格), L 是一个给定整数。要求给出一个排版方案使得排版后各行的不整齐度之和最小。

    【朴素的动态规划】

    用状态 F[i] 表示前 i 行经过排版后的最小不整齐度。转移方程:

    F[i] = min{F[j] + (S[i]-S[j]+i-j-1-L)^2 }

    其中 S[i] 表示前 i 行的句子长度之和。

    【凸壳优化的动态规划】

    A[i]=S[i]+i,B[i]=S[i]+i-L-1

    则方程可转化为 F[j]=min{F[j] + A[j]^2 + B[i]^2 - 2*A[j]*B[i]}

    对于 F[i] 的任意两个决策 p,q(0<p<q<i) ,决策 p 优于 q 的充分必要条件是

    F[p] + A[p]^2 + B[i]^2 - 2*A[p]*B[i] < F[q] + A[q]^2 + B[i]^2 - 2*A[q]*B[i]

    整理得

    2*B[i]*(A[q]-A[p])<(F[q]+A[q]^2) - (F[p]+A[p]^2)

    再令 C[i]=F[i]+A[i]^2

    则上式可化为

    (C[q]-C[p]) / (A[q]-A[p]) > 2*B[i]

    (A[i],C[i]) 看成平面上的点,则上式左边即为斜率。因此,最优决策必满足,所有他左边的决策点与他连线的斜率小于等于 2*B[i] ,所有他右边的决策点与他连线的斜率大于 2*B[i] 。显然最优决策位于决策点 1..i-1 形成的下凸的凸壳上,因此只需维护一个维护一个下凸的凸壳就可以让决策均摊时间复杂度变为 O(1)

    【程序】

    #include<cstdio> #include<cstring> #include<cmath> using namespace std; typedef long long big; const big MAXN=100020,MAXL=50; const big LIMIT=1000000000000000000LL; big T,N,L,P,S[MAXN],A[MAXN],B[MAXN],Dec[MAXN]; double C[MAXN],F[MAXN]; big Ans; bool isdec[MAXN]; char sent[MAXN][MAXL]; FILE *fi,*fo; struct queue { big head,tail; struct point { big id,x; double y; }V[MAXN]; inline void init() { head=1; tail=0; } inline double getk(point p,point q) { return (q.y-p.y)/(double(q.x-p.x)); } void ins(big id,big x,double y) { point tmp={id,x,y}; while (head<tail && getk(V[tail-1],V[tail])>=getk(V[tail],tmp)) tail--; V[++tail]=tmp; } big get(double k) { while (head<tail && getk(V[head],V[head+1])<=k) head++; return V[head].id; } }Q; void init() { fscanf(fi,"%lld%lld%lld",&N,&L,&P); S[0]=0; for (int i=1; i<=N; i++) { fscanf(fi,"%s",&sent[i]); S[i]=S[i-1]+strlen(sent[i]); A[i]=S[i]+i; B[i]=S[i]+i-L-1; } Q.init(); } void solve() { F[0]=0; Q.ins(0,0,0); for (int i=1; i<=N; i++) { big j=Q.get(2*B[i]); Dec[i]=j; F[i]=F[j]+pow(S[i]-S[j]+i-j-1-L,2); C[i]=F[i]+A[i]*A[i]; Q.ins(i,A[i],C[i]); } } void print() { if (F[N]>LIMIT) fprintf(fo,"Too hard to arrange"); else { Ans=big(floor(F[N])); fprintf(fo,"%lld/n",Ans); memset(isdec,0,sizeof(isdec)); for (int i=N; i; i=Dec[i]) isdec[i]=true; for (int i=1; i<=N; i++) { fprintf(fo,"%s",sent[i]); if (!isdec[i]) fprintf(fo," "); else fprintf(fo,"/n"); } for (int i=1; i<=20; i++) fprintf(fo,"%c",char(45)); fprintf(fo,"/n"); } } int main() { fi=fopen("poet.in","r"); fo=fopen("poet.out","w"); fscanf(fi,"%lld/n",&T); while (T--) { init(); solve(); print(); } fclose(fi); fclose(fo); }

     

     


    最新回复(0)