题目大意 一个数组,选一些数删除,求max(删除的数的和,除删除的数外连续子序列的和)
1 4 5 3 3 2
删除2 5位置上的数 max(4 + 3, 1 , 5 + 3, 2) = 8;
首先想到二分答案,第一个样例当x = 7 的时候正好满足,但是我们发现样例二,它答案是5,
1 2 3 4 5 如果按我们的思路当x = 5时 不满足,它在没有遍历后面的时候直接把 1 先删去了,需要优化check函数,所以现在我们需要动态规划的思想。
dp[i] 表示 删除第 i 个数 使前面全部满足的最小花费,
dp[i] = min(j ~ i , dp[j]) + a[i],(sum[j + 1, i - 1] <= x) 在连续子序列满足的情况下,求删除数的最小花费,如果花费小于x 返回true。
但是需要优化找最小的dp[j]的过程,所以需要使用优先队列来存一下每个满足条件的dp[i];
二分 + dp + 优先队列
#include <iostream>
#include <bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N = 2e5 + 10;
int t;
int n;
int dp[N];
int a[N];
typedef pair<int,int> pll;
bool check(int x)
{
int sum = 0;
int now = 0;
int l = 1;
priority_queue<pll, vector<pll>,greater<pll>> q;
q.push({0 , 0});
for(int r = 1;r <= n + 1;r ++){
while(now > x){
now -= a[l];
l ++;
}
while(!q.empty() && q.top().second < l - 1)q.pop();
dp[r] = q.top().first + a[r];
q.push({dp[r] , r});
now += a[r];
}
//for(int i = 1;i <= n + 1;i ++) cout << dp[i] << ' ';
//cout << endl;
return dp[n + 1] <= x;
}
void solve()
{
cin >> n;
for(int i = 1;i <= n;i ++) cin >> a[i];
a[n + 1] = 0;
int l = 1,r = 1e14;
while(l + 1 < r)
{
int mid = (l + r) / 2;
if(check(mid))r = mid;
else l = mid;
}
if(check(l))cout << l << endl;
else cout << r << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
while(t --)solve();
return 0;
}