ABC345E

不得不说这道 dp 既考察对时间复杂度方面的优化,也要考虑对空间方面的优化。

题意

首先从暴力说起:

首先既然让我们删除 \(k\) 个数,也就是说保留 \(N-k\) 个数。
显然可以 dfs 枚举子集加剪枝优化(不过剪不剪的好像没啥区别),这样做是 \(O(2^N)\) 估计只能过样例。

代码:

//tomxi
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5; 
bitset<N> v;
int val[N],mx=-1,col[N],n,k;
inline void dfs(int x,int K){
	if(K>k) return;
	if(K+(n-x+1)<k) return;
	if(x==n+1){
		if(K!=k) return;
		int pos=-1,s=0;
		for(int i=1;i<=n;i++){
			if(v[i]){
				if(col[i]==pos) return;
				pos=col[i];
				s+=val[i];
			}
		}
		mx=max(mx,s);
		return;
	}
	for(int i=0;i<=1;i++){
		v[x]=i;
		dfs(x+1,K+(!i));
	}
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin>>n>>k;
	for(int i=1;i<=n;i++) cin>>col[i]>>val[i];
	dfs(1,0);
	cout<<mx;
	return 0;
}
//tomxi

发现如果一个数选肯定是比不选要优的所以我们可以考虑尽可能的选第 \(i\) 个数,因此考虑 dp
暴力的 dp:
一个最最最暴力的做法就是定义 \(f_{i,j}\) 表示在前 \(i\) 个数中选了 \(j\) 个数且第 \(i\) 个数必须选,那么一个很显然的转移方程就是 \(f_{i,j} = val_i + \max_{x=1}^{i-1} f_{x,j-1}\) 。这样做很显然是 \(O(N^2K)\) 的,过不了。

代码:

//tomxi
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e3+5;
const int K=2e3+5;
int f[N][K]; 
int col[N],val[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int n,k;
	cin>>n>>k;
	int maxn=0,tm=0;
	for(int i=1;i<=n;i++) cin>>col[i]>>val[i];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n-k;j++){
			maxn=0;int stm=1;
			f[i][j]=val[i];
			for(int p=1;p<i;p++){
				if(col[p]!=col[i])maxn=max(maxn,f[p][j-1]);
				if(col[p]!=col[i]) stm++;
			}
			tm=max(tm,stm);
			f[i][j]+=maxn;
		}
	}
	int mx=0;
	if(tm<n-k){
		cout<<-1;return 0;
	}
	for(int i=1;i<=n;i++) mx=max(mx,f[i][n-k]);
	cout<<mx;
	return 0;
}
//tomxi

时间复杂度方面优化 dp:
其实我们发现我们选的区间都是连续的区间,因此可以考虑用数据结构来优化它,固然考虑线段树,但是因为如果最大值的 col[]col[i] 相同的话就要考虑使用次大值了,这样做的话要考虑区间最大值和严格区间次大值,处理起来相当麻烦,关键是还过不了,时间复杂度是 \(O(Nk\log_2N)\)
但其实并不需要这么麻烦,在仔细观察一下,可以发现所取的区间不光是连续的,而且还是从 1i 的,因此我们可以用两个变量来储存所对应的最大值,次大值,最大值的位置,次大值的位置。

空间方面的优化 dp:

因为 \(k\leq 500\) 所以 \(N-k\) 会很大,因此如果我们开二维的数组会到达 \(O(4\times 10^{10})\) 是不行的,因此考虑用滚动数组优化它。

代码:

//tomx
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N=2e5+5;
int n,k,col[N],val[N];
ll f[N];
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);cout.tie(nullptr);
  cin>>n>>k;
  for(int x=1;x<=n;++x) cin>>col[x]>>val[x];
  memset(f,-1e18,sizeof(f));
  for (int y=1;y<=n-k;++y) {
    ll m=f[y-1],_m=-1e18;
    int cm=col[y-1];
    for (int z=y;z<=y+k;++z){
      ll M=f[z];
      if(col[z]!=cm) {
        f[z]=m+val[z];
        if(M>=m){
          _m=m,m=M,cm=col[z];
        }else if(M>_m){
          _m=M;
        }
      }else{
        f[z]=_m+val[z];
        if(M>m){
          m=M;
        }
      }
    }
  }
  ll ans=-1;
  for(int i=n-k;i<=n;++i) {
    ans=max(ans,f[i]);
  }
  cout<<ans;
  return 0;
}
posted @ 2024-03-22 19:50  tomxi  阅读(17)  评论(0编辑  收藏  举报