题目描述
小 G 是一个出色的诗人,经常作诗自娱自乐。但是,他一直被一件事情所困扰,那就是诗的排版问题。
一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的。小 G 给每首诗定义了一个行标准长度(行的长度为一行中符号的总个数),他希望排版后每行的长度都和行标准长度相差不远。显然排版时,不应改变原有的句子顺序,并且小 G 不允许把一个句子分在两行或者更多的行内。在满足上面两个条件的情况下,小 G 对于排版中的每行定义了一个不协调度, 为这行的实际长度与行标准长度差值绝对值的 P 次方,而一个排版的不协调度为所有行不协调度的总和。
小 G 最近又作了几首诗,现在请你对这首诗进行排版,使得排版后的诗尽量协调(即不协调度尽量小),并把排版的结果告诉他。
几句废话
一个月前的坑,终于来补了
之前死活没听懂,现在感觉很简单
解题思路
首先把DP式列出来
设
F
[
i
]
F[i]
F[i] 表示以第i个句子作为某一个结尾的最小不协调度,
s
u
m
[
i
]
sum[i]
sum[i]表示1~i的字符串的长度总和
F
[
i
]
=
m
i
n
(
F
[
j
]
+
∣
s
u
m
[
i
]
−
s
u
m
[
j
]
+
i
−
j
−
1
−
L
∣
P
)
F[i]=min(F[j]+\vert sum[i]-sum[j]+i-j-1-L\vert^P)
F[i]=min(F[j]+∣sum[i]−sum[j]+i−j−1−L∣P)
也很好理解吧,就是枚举上一个位置,当前行的长度就是字符串的长度和+空格个数
这个式子有高次项,不好优化,于是
通过简单的打表我们发现它满足一个很重要的性质
决策单调性
其含义为,若对于 J ,他的最优决策点为 i ,则 J 后面的位置的最优决策点一定大于等于 i
或者可以理解为
1
2
3
4
5
1\;\;\;2\;\;\;3\;\;4\;\;5
12345
p
1
p
2
p
3
p
4
p
5
p_1\;p_2\;p_3\;p_4\;p_5
p1p2p3p4p5
上边表示每个位置,下边是每个位置的最优决策(也就是从那个点转移更优)
决策单调性的含义就是
p
1
≤
p
2
≤
p
3
≤
p
4
≤
p
5
p_1 \leq p_2 \leq p_3 \leq p_4 \leq p_5
p1≤p2≤p3≤p4≤p5
有了这个性质我们就来优化吧
可以发现在任意时刻,全局的最有决策一定是块状分布的
所以我们用一个队列维护这些块
最初只有一个元素,范围为1~n,这些点都从0转移更优
然后每加一个元素,就取队头的决策点就可以了,然后把不合法的块删掉
因为决策单调性,所以当前位置如果成为最优决策点,一定是某一个后缀
那就从后开始检查,如果这个块左端点从i转移更优,就把这个块删掉
如果找的了某个块,不优了那么就在这个块内二分,找到第一个从i转移更优的位置,然后把这块从那个位置分裂,并把后面加一个到n的快,这里由i转移更优
坑点
本题如果中间就大于1e18 那么就炸了
所以要用long double 就好了
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LD;
const LL N = 1e5+7;
struct node
{
LL l,r,j;
};
char s[N][50];
LL sum[N],len[N],n,L,p;
LD F[N];
LL Pos[N];
deque<node> q;
node Node(LL j,LL l,LL r)
{
node tmp;
tmp.j=j;
tmp.l=l;
tmp.r=r;
return tmp;
}
LD Pow(LD a,LL b)
{
LD res=1;
while(b)
{
if(b&1) res=res*a;
a=a*a;
b/=2;
}
return res;
}
LD val(LL i,LL j)
{
return F[j]+Pow(llabs(sum[i]-sum[j]+i-j-1-L),p);
}
LL cnt=0;
LL Find(LL i,LL j,LL L,LL R)
{
cnt++;
LL l=L,r=R,mid,ans=L-1;
while(l<=r)
{
mid=(l+r)>>1;
if(val(mid,j)<val(mid,i))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
return ans;
}
LL seq[N],top=0;
void solve()
{
scanf("%lld%lld%lld",&n,&L,&p);
memset(F,0,sizeof(F));
memset(Pos,0,sizeof(Pos));
for(LL i=1;i<=n;i++)
{
scanf("%s",s[i]+1);
len[i]=strlen(s[i]+1);
sum[i]=sum[i-1]+len[i];
}
while(!q.empty()) q.pop_back();
q.push_back(Node(0,1,n));
for(LL i=1;i<=n;i++)
{
while(!q.empty()&&q.front().r<=i-1) q.pop_front();
q.front().l=i+1;
LL j=q.front().j;
F[i]=val(i,j);
Pos[i]=j;
while(!q.empty()&&val(q.back().l,q.back().j)>=val(q.back().l,i)) q.pop_back();
LL pos=0;
if(!q.empty())
{
pos=Find(i,q.back().j,q.back().l,q.back().r);
q.back().r=pos;
}
if(pos+1<=n) q.push_back(Node(i,pos+1,n));
}
top=0;
for(LL i=n;i;i=Pos[i])
seq[++top]=i;
seq[top+1]=0;
if(F[n]>1e18) printf("Too hard to arrange\n");
else
{
printf("%lld\n",(LL)(F[n]+0.5));
for(LL i=top;i>=1;i--)
{
for(LL j=seq[i+1]+1;j<seq[i];j++)
{
printf("%s ",s[j]+1);
}
printf("%s\n",s[seq[i]]+1);
}
}
printf("--------------------\n");
}
int main()
{
LL T;
scanf("%lld",&T);
while(T--)
{
solve();
}
return 0;
}