NOIAC D2T2#61sequence k-交换最大子段和及其方案输出

#61sequence

题目传送门

分析

题目大意:给一个序列 A,你可以进行 k 次交换操作,最大化最大子段和,要输出方案有spj。
神仙数位Dp
首先交换这种东西一般就是放宽条件,变成有若干个数可以换出当前区间,若干个数可以换进当前区间,最后采用换进换出相同个数的数的状态即可。
f [ i ] [ p ] [ q ] [ r ] f[i][p][q][r] f[i][p][q][r]表示前 i i i个数,换进 p p p个数,换出 q q q个数,选择的子段和区间在当前位置 i i i的前面,中间,还是后面( r = 0 , 1 , 2 r=0,1,2 r=0,1,2)
考虑转移,需要大力讨论。

#Case1:r=1

当前 i i i在选择的区间之中。
有以下几种转移

  • 接在上一个数的后面。 f [ i ] [ p ] [ q ] [ 1 ] = f [ i − 1 ] [ p ] [ q ] [ 1 ] + a [ i ] f[i][p][q][1]=f[i-1][p][q][1]+a[i] f[i][p][q][1]=f[i1][p][q][1]+a[i]
  • 之前一个数还没选,现在刚刚要选这个数: f [ i ] [ p ] [ q ] [ 1 ] = f [ i − 1 ] [ p ] [ q ] [ 0 ] + a [ i ] f[i][p][q][1]=f[i-1][p][q][0]+a[i] f[i][p][q][1]=f[i1][p][q][0]+a[i]
  • 上一个数还在选,但是不想选这个数,把一个数换进来替换它: f [ i ] [ p ] [ q ] [ 1 ] = f [ i − 1 ] [ p − 1 ] [ q ] [ 1 ] f[i][p][q][1]=f[i-1][p-1][q][1] f[i][p][q][1]=f[i1][p1][q][1]
  • 上一个数还没选,也不想选这个数,但是想在这个位置开始选,仍然是把一个数换进来替换它: f [ i ] [ p ] [ q ] [ 1 ] = f [ i − 1 ] [ p − 1 ] [ q ] [ 0 ] f[i][p][q][1]=f[i-1][p-1][q][0] f[i][p][q][1]=f[i1][p1][q][0]

r = 1 r=1 r=1的情况讨论完了。注意换进来的数的贡献在后面统计。

#Case2:r=0,2

这个时候当前的位置是不选的,所以如果一个数想要有贡献一定得换走。

  • 不想做任何动作: f [ i ] [ p ] [ q ] [ r ] = f [ i − 1 ] [ p ] [ q ] [ r ] f[i][p][q][r]=f[i-1][p][q][r] f[i][p][q][r]=f[i1][p][q][r]
  • 之前选,现在不选: f [ i ] [ p ] [ q ] [ 2 ] = f [ i − 1 ] [ p ] [ q ] [ 1 ] f[i][p][q][2]=f[i-1][p][q][1] f[i][p][q][2]=f[i1][p][q][1]
  • 把当前这个数换到可以选的某个位置: f [ i ] [ p ] [ q ] [ r ] = f [ i − 1 ] [ p ] [ q − 1 ] [ r ] + a [ i ] f[i][p][q][r]=f[i-1][p][q-1][r]+a[i] f[i][p][q][r]=f[i1][p][q1][r]+a[i]
  • 之前选,现在位置不选,但是马上把这个数换走拿去充贡献: f [ i ] [ p ] [ q ] [ 2 ] = f [ i − 1 ] [ p ] [ q − 1 ] [ 1 ] + a [ i ] f[i][p][q][2]=f[i-1][p][q-1][1]+a[i] f[i][p][q][2]=f[i1][p][q1][1]+a[i]

其中 r r r表示 0 , 2 0,2 0,2都有这步转移,否则的话是由 1 − > 2 1->2 1>2
最后的答案就是 M a x f [ n ] [ i ] [ i ] [ 1 , 2 ] Max f[n][i][i][1,2] Maxf[n][i][i][1,2]

输出

之所以定义如此麻烦的状态,就是因为其特别好输出。
考虑刚才的状态,实际上本质上都是两种请况。

  • 有无分界点
  • 有无交换

所以可以用一个两位2进制数来表示当前状态的选择是什么。
倒着扫一遍输出即可。

代码

#include<bits/stdc++.h>
#define rep(i, j, k) for(int i = j;i <= k; ++i) 
const int N = 1e5 + 10, M = 11;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int n, m, a[N], pr, cu; char g[N][M][M][3], *B;
long long f[2][M][M][3], inf, *A;
void Tra(long long x, int c) {if(x > *A) *A = x, *B = c;}
int main() {
	n = ri(); m = ri();
	for(int i = 1;i <= n; ++i) a[i] = ri();
	memset(f, -0x3f, sizeof(f)); memset(g, -1, sizeof(g));
	inf = -f[0][0][0][0]; f[0][0][0][0] = 0; pr = 0, cu = 1;
	for(int i = 1;i <= n; ++i, std::swap(cu, pr)) {
		memset(f[cu], -0x3f, sizeof(f[cu]));
		rep(j, 0, m) rep(k, 0, m) rep(l, 0, 2) {
			A = &f[cu][j][k][l]; B = &g[i][j][k][l];
			if(l == 1) {
				Tra(f[pr][j][k][l] + a[i], 0);
				Tra(f[pr][j][k][0] + a[i], 1);
				if(j) Tra(f[pr][j - 1][k][l], 2);
				if(j) Tra(f[pr][j - 1][k][0], 3);
			}
			else {
				Tra(f[pr][j][k][l], 0);
				if(l == 2) Tra(f[pr][j][k][1], 1);
				if(k) Tra(f[pr][j][k - 1][l] + a[i], 2);
				if(l == 2 && k) Tra(f[pr][j][k - 1][1] + a[i], 3);
			}
		}
	}
	long long Ans = -inf; int p, q, r, L, R = 0;
	rep(i, 0, m) rep(j, 1, 2) 
		if(Ans < f[pr][i][i][j]) 
			Ans = f[pr][i][i][j], p = q = i, r = j;
	printf("%lld %d\n", Ans, p);
	std::vector<int>x, y;
	for(int i = n; i; --i) {
		int c = g[i][p][q][r];
		if(r == 1 && !R) R = i;
		if(c & 2) {
			if(r == 1) --p, x.push_back(i);
			else --q, y.push_back(i);
		}
		if(c & 1) !--r ? L = i : 0;
	}
	for(int i = 0;i < x.size(); ++i) printf("%d %d\n", x[i], y[i]);
	printf("%d %d\n", L, R);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值