bzoj上这道题是权限题,然鹅我没权限
AcWing上有:https://www.acwing.com/problem/content/description/306/
对于普通的dp方程,很快可以写出:
设句子长度的前缀和为sum数组,句子长度数组为a数组
dp[i]:以句子i结尾的最小不协调度 , len :标准长度
但很明显这样的时间复杂度是O(N^2) ,过不了
可以用决策单调性来优化dp
决策单调性的前提需要满足四边形不等式。
决策单调性大概就是假如dp[i]以dp[j]为最优决策更新来源,则对于所有x>i,dp[x]的最优决策来源>=j
先(粗略)的证明一下可以使用决策单调性。
先对式子变一下型,相同的凑一起:
则需要证明:
观察val的形态,决定让i相同的项相同的凑一起
式子变形:
展开,设val(i+1,j) = x val(i,j)= y
因为x>y,所以只需要证明
在t>0的地方单调递增(ps:我上面的式子好像漏写了绝对值符号QAQ)
对其求导:
很明显当p,t,c都大于0的时候式子大于0.
因为我使用的是优先队列,所以还不需要二分寻找
而具体过程大概为:
使用(loc,l,r) 表示 l,r 区间的最优决策来源dp[j] 的下标j=loc
一开始先插入(0,1,n),然后for(int i=1;i<=n;i++)进行遍历
对于每一次遍历:
1.先排除过时决策,排除队列左端(loc,i,j) 中j<i 的
2.试取队列左端作为dp[i]的最优选择
3.取出队尾,记为(locr,lr,rr),然后以dp[i]更新决策,具体表现为:
if以dp[i]更新lr优于以dp[locr]来更新,那么最右边的决策就不要了
else if以dp[i]更新rr优于以dp[locr]来更新,就(看代码,感觉不好说明。。)
else 二分查找lr,rr,找出一个点w使得小于w部分以dp[locr]更新更好,大于等于w部分以dp[i]来更新更好,然后修改rr=w-1
然后插入新决策
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
#define ld long double
int n, len, p;
int sum[maxn];
ld dp[maxn];
const ld ed = 1e18;
struct node {
int l, r, loc;
}q[maxn];
ld calc(int i, int j) {// i : 前一次结束的位置 j:后一次结束的位置
//int flag = 1;
ld ans = 1;
ld num = abs((ld)sum[j] - sum[i] + j - i - 1 - len);
for (int i = 1; i <= p; i++) ans *= num;
return ans + dp[i];
}
void insert(int i, int& L, int& R) {
int flag = -1;
while (L <= R) {
if (calc(q[R].loc, q[R].l) >= calc(i, q[R].l)) flag = q[R--].l;
else {
if (calc(q[R].loc, q[R].r) > calc(i, q[R].r)) {
int ll = q[R].l, rr = q[R].r;
while (ll < rr) {
int mid = (ll + rr) >> 1;
if (calc(i, mid) > calc(q[R].loc, mid)) ll = mid + 1;
else {
rr = mid;
}
}
q[R].r = ll - 1;
flag = ll;
}
break;
}
}
if (flag != -1) { //加入新决策
q[++R].loc = i;
q[R].l = flag;
q[R].r = n;
}
}
int main() {
int t; cin >> t;
while (t--) {
scanf("%d %d %d", &n, &len, &p);
sum[0] = 0;
for (int i = 1; i <= n; i++) {
char s[50];
scanf("%s", s);
sum[i] = strlen(s) + sum[i - 1];
}
int q_l = 1, q_r = 0;
q[++q_r].l = 1;
q[q_r].r = n;
q[q_r].loc = 0;
for (int i = 1; i <= n; i++) {
while (q_l < q_r && q[q_l].r < i) //排除过时决策
q_l++;
q[q_l].l = i + 1;
dp[i] = calc(q[q_l].loc, i);
insert(i, q_l, q_r);
}
if (dp[n] > ed) {
cout << "Too hard to arrange" << endl;
}
else {
cout << (long long)dp[n] << endl;
}
cout << "--------------------" << endl;
}
return 0;
}