E. Maximum Monogonosity

You are given an array aa of length nn and an array bb of length nn. The cost of a segment [l,r][l,r], 1≤l≤r≤n1≤l≤r≤n, is defined as |bl−ar|+|br−al||bl−ar|+|br−al|.

Recall that two segments [l1,r1][l1,r1], 1≤l1≤r1≤n1≤l1≤r1≤n, and [l2,r2][l2,r2], 1≤l2≤r2≤n1≤l2≤r2≤n, are non-intersecting if one of the following conditions is satisfied: r1<l2r1<l2 or r2<l1r2<l1.

The length of a segment [l,r][l,r], 1≤l≤r≤n1≤l≤r≤n, is defined as r−l+1r−l+1.

Find the maximum possible sum of costs of non-intersecting segments [lj,rj][lj,rj], 1≤lj≤rj≤n1≤lj≤rj≤n, whose total length is equal to kk.

Input

Each test consists of multiple test cases. The first line contains a single integer tt (1≤t≤1000)(1≤t≤1000) — the number of sets of input data. The description of the test cases follows.

The first line of each test case contains two integers nn and kk (1≤k≤n≤30001≤k≤n≤3000) — the length of array aa and the total length of segments.

The second line of each test case contains nn integers a1,a2,…,ana1,a2,…,an (−109≤ai≤109−109≤ai≤109) — the elements of array aa.

The third line of each test case contains nn integers b1,b2,…,bnb1,b2,…,bn (−109≤bi≤109−109≤bi≤109) — the elements of array bb.

It is guaranteed that the sum of nn over all test case does not exceed 30003000.

Output

For each test case, output a single number — the maximum possible sum of costs of such segments.

我比赛时写的是o(nk^2) 的比较常规的转移

dp[n1][k1]=max(dp[n1−1][k1],dp[n1−l][k1−l]+f(n1−l+1,n1),1≤l≤k1)

看别人写之后才发现题目太妙了

首先我们不要把目光光顾着所有个区间,你去看方程的含义

其实化简出来有4中不同的方程

bl-ar+br-al

ar-bl+br-al

ar-bl+al-br

bl-ar+al-br

那bl,al来说有4种不同的状态(1,1),(1,-1),(-1,1),(-1,-1)

因为bl和ar相对应,al和bl相对应,那么br,br的状态恰好和ar,al的相反

所有整个方程有4种状态

那我们怎么知道要用哪种状态呢

我们用dp[4010][4010][5]来存储

dp[i][j][k]--》i表示当前到达第i位,j表示当前取j个元素,k(0~3)用状态压缩表示当第j个元素取al和bl的状态以及前面i-1位中取j-1个元素的最佳状态的和,k(4)表示当前取第i位取j个元素的最佳状态

因为我们上面说你知道ar,al的状态就能知道br,bl的状态:比如bl,al=(1,-1)则br,ar=(1,-1)

因为所有你通过枚举b就能知道当前的最大值

#include<iostream>
#include<algorithm>
#include<numeric>//accumulate(be,en,0)
#include<cstring>//rfind("string"),s.find(string,begin)!=s.npos,find_first _of(),find_last_of()
#include<string>//to_string(value),s.substr(int begin, int length);
#include<cstdio>
#include<cmath>
#include<vector>//res.erase(unique(res.begin(), res.end()), res.end()),reverse(q.begin(),q.end()),vector<int>().swap(at[mx])
#include<queue>//priority_queue(big)  /priority_queue<int, vector<int>, greater<int>> q(small)
#include<stack>
//#include<map>//unordered_map
#include<set>//iterator,insert(),erase(),lower(>=)/upper_bound(>)(value)/find()return end()
#include<unordered_map>
#include<unordered_set>
#include<bitset>//size,count(size of 1),reset(to 0),any(have 1?)
//#include<ext/pb_ds/assoc_container.hpp>//gp_hash_table
//#include<ext/pb_ds/hash_policy.hpp>
//using namespace __gnu_pbds;
#define int long long//__int128 2^127-1(GCC)
#define PII pair<int,int>
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f, N = 3000 + 5, mod = 1e9 + 7;
int dp[N][N][5];
signed main()
{
	ios_base::sync_with_stdio(0); cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		int n, k;
		cin >> n >> k;
		vector<int>a(n), b(n);
		for (int& x : a) cin >> x;
		for (int& x : b) cin >> x;
		for (int i = 0; i < n + 1; i++) {
			for (int j = 0; j <= k; j++) {
				fill(dp[i][j], dp[i][j] + 5, -inf);//最开始初始化为负无穷
			}
		}
		dp[0][0][4] = 0;//第0位取0个的最佳状态是0
		for (int i = 0; i < n; i++) {//从0~n-1枚举位数
			for (int j = 0; j < k; j++) {//取j个转移到取j+1个
				for (int mask = 0; mask < 4; mask++) {//4a[l],b[l]个状态
					int s1 = (mask & 1 ? -1 : 1);//a[i]的状态
					int s2 = (mask & 2 ? -1 : 1);//b[i]的状态
					dp[i][j + 1][mask] = max(dp[i][j + 1][mask], dp[i][j][4] + s1 * a[i] + s2 * b[i]);
//第i位取j+1个是从第i位取j个的最佳状态转移过去的
				}
			}
			for (int j = 0; j <= k; j++) {//当前取j个
				for (int mask = 0; mask < 4; mask++) {//枚举b[r],a[r]的状态
					int s1 = (mask & 1 ? -1 : 1);//b[r]
					int s2 = (mask & 2 ? -1 : 1);//a[r]
					dp[i][j][4] = max(dp[i][j][4], dp[i][j][mask] - s2 * a[i] - s1 * b[i]);
//因为mask的状态只有a[l],b[l]是不完整的最大值,我们从不完整的加上b[r],a[r]就成了完整的状态就
//是转移到dp[i][j][4]中表示前i为取j个的最大值
					if (j != k) dp[i + 1][j + 1][mask] = dp[i][j][mask];
//如果取的个数不到k个的话,不完整的状态就可以继续往下传递
				}
				dp[i + 1][j][4] = max(dp[i + 1][j][4], dp[i][j][4]);
//最后前i个取j个的最大值转移到前i+1个取j个的最大值
			}
		}
		cout << dp[n][k][4] << '\n';
//输出前n个取k个的最大值
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值