codeforces 1442 D sum (退背包)

题目链接:https://codeforces.com/contest/1442/problem/D

题目大意:

给定 \(n\) 个元素单调不降的序列,只能从这些序列的最左端依次取数,问取 \(k\) 个数的最大值

题解:

首先考虑朴素 \(dp\), 设 \(dp[i][j]\) 表示前 \(i\) 个序列取了 \(j\) 个数的最大值,复杂度 \(O(nk^2)\)

发现序列单调不降的性质没有用到,假设有一种取 \(k\) 个数的方案,若取了第 \(i,j\) 个序列的元素,我们发现总可以通过调整使得其中一个序列的元素被取完,最后发现,最优的方案一定是:有一个序列的取部分元素,其他序列要么全取,要么不去取

于是,把每个序列看做一个物品,枚举哪个序列取了一部分,就转化成了退背包模型,分治即可

时间复杂度 \(O(knlogn)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 3010;

int n, k;
int t[maxn];
vector<ll> a[maxn], sum[maxn];
ll dp[maxn], tmp[maxn][maxn];

ll ans = 0;

void solve(int l, int r, int d){
	if(l == r){
		for(int i = 0 ; i <= k ; ++i){
			if(i + t[l] >= k) ans = max(ans, dp[i] + sum[l][k - i]);
		}
		return;
	}
	int mid = (l + r) >> 1;
	
	for(int i = 0 ; i <= k ; ++i) tmp[d][i] = dp[i];
	
	for(int i = mid + 1 ; i <= r ; ++i){
		for(int j = k ; j >= t[i] ; --j){
			dp[j] = max(dp[j], dp[j - t[i]] + sum[i][t[i]]);
		}
	}
	solve(l, mid, d + 1);
	for(int i = 0 ; i <= k ; ++i) dp[i] = tmp[d][i];
	
	for(int i = 0 ; i <= k ; ++i) tmp[d][i] = dp[i];
	for(int i = l ; i <= mid ; ++i){
		for(int j = k ; j >= t[i] ; --j){
			dp[j] = max(dp[j], dp[j - t[i]] + sum[i][t[i]]);
		}
	}
	solve(mid + 1, r, d + 1);
	for(int i = 0 ; i <= k ; ++i) dp[i] = tmp[d][i];
}

ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }

int main(){
	memset(dp, -0x3f, sizeof(dp));
	dp[0] = 0;
	n = read(), k = read();
	for(int i = 1 ; i <= n ; ++i){
		t[i] = read();
		a[i].push_back(0);
		sum[i].push_back(0);
		for(int j = 1 ; j <= t[i] ; ++j){
			ll x = read();
			a[i].push_back(x);
			sum[i].push_back(sum[i][j - 1] + x);
		}
	}
	
	solve(1, n, 0);
	
	printf("%lld\n", ans);
	
	return 0;
}
posted @ 2021-01-13 11:57  Tartarus_li  阅读(132)  评论(0编辑  收藏  举报