zoukankan      html  css  js  c++  java
  • JZOJ 6974. 【2021.02.01冬令营模拟】联邦解体(拆绝对值+DP)

    JZOJ 6974. 【2021.02.01冬令营模拟】联邦解体

    题目大意

    • 长为 n n n的原序列中每个数有两个权值 A i , B i A_i,B_i Ai,Bi,求保持相对顺序不变的前提下,把原序列分割为两个子序列后所有相邻两项的 A , B A,B A,B权值之差的绝对值之和的最大值。
    • n ≤ 1 0 6 nle10^6 n106

    题解

    • 既有绝对值,又要求最大值,考虑到若不取绝对值的话,一定不会比真实答案更优,所以可以去掉绝对值号,而只需保证相邻两项的 A A A B B B对答案的贡献符号相反,注意这里的贡献并不是每个数只贡献一次,而是与其左边和右边的数分别贡献一次,当且仅当边界处才贡献一次。
    • 可以考虑DP,不需要记录两个序列中当前最后一个数分别是什么,只需记两个序列的最后一个数的 A , B A,B A,B分别取了什么符号,共 2 ∗ 2 ∗ 2 ∗ 2 = 16 2*2*2*2=16 2222=16种可能,但因为左右边界都少贡献一次,所以还要设两种状态表示是否为左右边界,所以状态一共为 6 ∗ 6 = 36 6*6=36 66=36种。
    • 转移的时候,先枚举上一位的状态,给当前的 A i , B i A_i,B_i Ai,Bi分别加上与上一位相反的贡献,再枚举当前位的贡献,再加上即可。对于左右边界的特殊情况,只需要简单的特判。

    代码

    #include<cstdio> 
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 1000010
    #define ll long long
    ll f[N][6][6], a[N], b[N];
    int read() {
    	int s = 0;
    	char x = getchar();
    	while(x < '0' || x > '9') x = getchar();
    	while(x >= '0' && x <= '9') s = s * 10 + x - 48, x = getchar();
    	return s;
    }
    int main() {
    	int n = read(), i, j;
    	for(i = 1; i <= n; i++) a[i] = read(), b[i] = read();
    	memset(f, 128, sizeof(f));
    	f[0][4][4] = 0;
    	for(i = 1; i <= n; i++) {
    		for(j = 0; j < 6; j++) {
    			ll s0 = f[i - 1][0][j], s1 = f[i - 1][1][j], s2 = f[i - 1][2][j], s3 = f[i - 1][3][j];
    			s0 += a[i] + b[i], s1 += a[i] - b[i], s2 += b[i] - a[i], s3 -= a[i] + b[i];
    			ll s4 = f[i - 1][4][j];
    			ll s = max(s4, max(max(s0, s1), max(s2, s3)));
    			f[i][0][j] = max(f[i][0][j], s - a[i] - b[i]);
    			f[i][1][j] = max(f[i][1][j], s - a[i] + b[i]);
    			f[i][2][j] = max(f[i][2][j], s + a[i] - b[i]);
    			f[i][3][j] = max(f[i][3][j], s + a[i] + b[i]);
    			f[i][5][j] = max(f[i][5][j], s);
    		}
    		for(j = 0; j < 6; j++) {
    			ll s0 = f[i - 1][j][0], s1 = f[i - 1][j][1], s2 = f[i - 1][j][2], s3 = f[i - 1][j][3];
    			s0 += a[i] + b[i], s1 += a[i] - b[i], s2 += b[i] - a[i], s3 -= a[i] + b[i];
    			ll s4 = f[i - 1][j][4];
    			ll s = max(s4, max(max(s0, s1), max(s2, s3)));
    			f[i][j][0] = max(f[i][j][0], s - a[i] - b[i]);
    			f[i][j][1] = max(f[i][j][1], s - a[i] + b[i]);
    			f[i][j][2] = max(f[i][j][2], s + a[i] - b[i]);
    			f[i][j][3] = max(f[i][j][3], s + a[i] + b[i]);
    			f[i][j][5] = max(f[i][j][5], s);
    		}
    	}
    	printf("%lld
    ", f[n][5][5]);
    	return 0;
    }
    

    自我小结

    • 拆绝对值已经不是第一次出现,套路很常见,要引起注意。
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    vuejs cli3 env配置文件实践指南
    Nginx的rewrite(地址重定向)剖析
    什么是TCP粘包?怎么解决这个问题
    windows bat批处理语法简析
    BAT文件语法和技巧(bat文件的编写及使用)
    Asyncio之EventLoop笔记
    python struct的使用例子
    redis慢查询笔记
    redis基础操作概念等笔记
    Python实现Dijkstra算法
  • 原文地址:https://www.cnblogs.com/LZA119/p/14437601.html
Copyright © 2011-2022 走看看