• 【luogu P1912】诗人小G(二分栈)(决策单调性优化DP)


    诗人小G

    题目链接:luogu P1912

    题目大意

    给你 n 句词,每一句有长度。
    然后你可以选择把若干首连续的句子放在一行,用空格隔开。
    然后一行的费用是它的长度(算上空格),跟标准长度的绝对值的 P 次方。
    一首诗的一个方法的费用是每行的费用和。
    然后要你求一首诗的最小费用,如果超过 1e18 特判一下,否则输出诗排布的方式。

    思路

    考虑 DP,设 f i f_i fi 把前 i i i 句放好的费用。
    然后 n 2 n^2 n2 转移。
    f i = min ⁡ { f j + ( s i − s j − 1 − L ) P } f_{i}=\min\{f_{j}+(s_i-s_{j}-1-L)^P\} fi=min{fj+(sisj1L)P}
    (其中 s s s 是前缀,包含空格,用 s i = s i − 1 + ∣ a i ∣ + 1 s_{i}=s_{i-1}+|a_i|+1 si=si1+ai+1 来转移)

    然后你想想一下就不难发现它满足决策单调性。
    然后你思考一下会发现它不太是那种直接单调的,所以我们要用一个叫做二分栈的东西。
    (因为它是根据 s j + 1 + L s_j+1+L sj+1+L 这个直线对称的,去掉绝对值变成两部分)

    然后这里说说二分栈是啥:
    就是你考虑对于栈中两个相邻的决策点,你通过二分得到一个临界值,在前面是这个优,后面是那个优。
    (每次要加一个的时候就先判断栈顶和它下面那个,栈顶的话就是新准备要加进去的二分)

    代码

    #include
    #include
    
    using namespace std;
    
    const int N = 1e5 + 100;
    const int P = 101;
    int n, L, p, sz[N], a[N], sta[N], R[N], fr[N];
    char s[N][P];
    long double f[N];
    
    int abs(int x) {return x < 0 ? -x : x;}
    long double ksm(long double x, int y) {
    	long double re = 1.0;
    	while (y) {
    		if (y & 1) re = re * x; x = x * x; y >>= 1; 
    	}
    	return re;
    }
    
    long double clac(int l, int r) {
    	return f[l] + ksm((long double)abs(a[r] - a[l] - 1 - L), p);
    }
    
    int Get_pl(int x, int y) {
    	int l = x, r = n, re = n + 1;
    	while (l <= r) {
    		int mid = (l + r) >> 1;
    		if (clac(x, mid) >= clac(y, mid)) re = mid, r = mid - 1;
    			else l = mid + 1;
    	}
    	return re;
    }
    
    void dfs(int now) {
    	if (!now) return ;
    	dfs(fr[now]);
    	for (int i = fr[now] + 1; i <= now; i++) {
    		for (int j = 1; j <= sz[i]; j++) putchar(s[i][j]);
    		if (i != now) putchar(' '); else putchar('\n');
    	}
    } 
    
    void slove() {
    	scanf("%d %d %d", &n, &L, &p);
    	for (int i = 1; i <= n; i++) {
    		scanf("%s", s[i] + 1); sz[i] = strlen(s[i] + 1);
    		a[i] = a[i - 1] + sz[i] + 1;
    	}
    	
    	int l = 1, r = 1;
    	for (int i = 1; i <= n; i++) {
    		while (l < r && R[sta[l]] <= i) l++;
    		f[i] = clac(sta[l], i); fr[i] = sta[l];
    		while (l < r && R[sta[r - 1]] >= Get_pl(sta[r], i)) r--;
    		R[sta[r]] = Get_pl(sta[r], i);
    		sta[++r] = i;
    	}
    	
    	if (f[n] > 1e18) printf("Too hard to arrange\n");
    		else {
    			printf("%.0Lf\n", f[n]);
    			dfs(n);
    		}
    }
    
    int main() {
    	int T; scanf("%d", &T);
    	while (T--) {
    		slove();
    		printf("--------------------\n");
    	}
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
  • 相关阅读:
    compose——侧边栏
    SpringCloud——OpenFeign(参数处理、传递时间参数处理、源码分析,日志处理)
    【光学】基于Matlab模拟干涉条纹图
    织信Informat如何连接其他应用?
    【视觉基础篇】14 # 如何使用片元着色器进行几何造型?
    Codeforces Round #818 (Div. 2)
    Java面试之JavaWeb常用框架(offer 拿来吧你)
    Idea:阿里巴巴Java编码插件
    深度之眼(三)——矩阵的行列式
    2.10 流程控制之 while循环
  • 原文地址:https://blog.csdn.net/weixin_43346722/article/details/126107947