1 条题解

  • 0
    @ 2025-8-24 21:49:04

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Alex_Wei
    **

    搬运于2025-08-24 21:49:04,当前版本为作者最后更新于2021-12-13 16:58:14,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    *P3446 [POI2006]EST-Aesthetic Text

    POI 合集

    还算有趣的动态规划。遇到这种区间划分题首先考虑 DP,设 fi,jf_{i,j} 表示以第 ii 个单词结尾为一行,且这一行长度为 jj 的最小答案,那么有 fi,j=mink=1m(fp,k+jk)f_{i,j}=\min_{\\k=1}^m(f_{p,k}+|j-k|),其中 pp 满足 ip1+q=p+1ilq=ji-p-1+\sum_{\\q=p+1}^i l_q=j。直接转移的复杂度为 O(n2m)\mathcal{O}(n^2m)(枚举 pp 算出对应的 jj)。

    不过我们注意到有很多 jj 都是空值,说明这个定义有些劣。不妨修改定义,设 fi,jf_{i,j} 表示最后一行为 ij+1ii-j+1\sim i 的答案,再设 Li,jL_{i,j} 表示 j1+p[ij+1,i]lpj-1+\sum_{\\p\in [i-j+1,i]}l_pij+1ii-j+1\sim i 组成一行的长度,那么有:

    $$f_{i,j}=\min_{p\in[1,i-j]}(f_{i-j,p}+|L_{i-j,p}-L_{i,j}|) $$

    此时时间复杂度优化为 O(n3)\mathcal{O}(n^3),但仍无法接受。接下来使用拆绝对值的技巧,即维护 prei,jpre_{i,j} 表示 fi,jLi,jf_{i,j}-L_{i,j}sufi,jsuf_{i,j} 表示 fi,j+Li,jf_{i,j}+L_{i,j},那么对于 fi,jf_{i,j} 对应的转移位置 iji-j,我们只需找到最后一个位置 pp 使得 Lij,pLi,jL_{i-j,p}\leq L_{i,j},那么 fi,jf_{i,j} 可以由 prei,ppre_{i,p} 关于 pp 的前缀 min\min 加上 Li,jL_{i,j},以及 sufi,p+1suf_{i,p+1} 关于 pp 的后缀 min\min 减去 Li,jL_{i,j} 得到。

    又因为固定 iji-j 时,Li,jL_{i,j} 随着 ii 增大而增大,这说明 fi+k,j+kf_{i+k,j+k}iji-j 处转移时的 pp 随着 kk 增加而递增,因此对每个位置 ii 在求出 fi,jf_{i,j} 维护一个转移指针即可做到平方。

    const int N = 2e3 + 5;
    int n, m, ans = 2e9, L[N], pos[N], tot[N], f[N][N];
    int len[N][N], pre[N][N], suf[N][N];
    int main(){
    	cin >> m >> n;
    	for(int i = 1; i <= n; i++) {
    		cin >> L[i], L[i] += L[i - 1];
    		for(int j = i - 1; ~j; j--) {
    			int cur = i - j - 1 + L[i] - L[j];
    			if(cur > m) break; tot[i]++;
    			if(!j) f[i][tot[i]] = 0;
    			else {
    				f[i][tot[i]] = 2e9;
    				while(pos[j] < tot[j] && len[j][pos[j] + 1] <= cur) pos[j]++;
    				if(pos[j]) cmin(f[i][tot[i]], pre[j][pos[j]] + cur);
    				if(pos[j] < tot[j]) cmin(f[i][tot[i]], suf[j][pos[j] + 1] - cur);
    			} len[i][tot[i]] = cur;
    		} pre[i][0] = suf[i][tot[i] + 1] = 2e9;
    		for(int j = 1; j <= tot[i]; j++) pre[i][j] = min(pre[i][j - 1], f[i][j] - len[i][j]);
    		for(int j = tot[i]; j; j--) suf[i][j] = min(suf[i][j + 1], f[i][j] + len[i][j]);
    		if(i == n) for(int j = 1; j <= tot[n]; j++) cmin(ans, f[i][j]);
    	} cout << ans << endl;
    	return 0;
    }
    
    • 1

    信息

    ID
    2523
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者