Monoxer Programming Contest 2024(AtCoder Beginner Contest 345)E. Colorful Subsequence(线性dp)

题目

n(n<=2e5)个球在同一行,第i个球颜色是ci(ci<=n),值是vi(vi<=1e9)

恰好移走k(k<=500)个球,剩下的球不改变顺序依次接上,

在满足剩下的球相邻两个球的颜色都不同的情况下,最大化剩余球的价值和

输出这个最大的价值和

如果没法满足颜色都不同的条件,输出-1

思路来源

赛后又仔细想了想就会了

题解

暴力是O(n*k*k)的,枚举当前颜色是什么,枚举上一个颜色是什么

但是仔细观察发现,实际前驱颜色只需要两种,即产生最大贡献的颜色,产生次大贡献的颜色

dp[i][j][2],其中dp[i][j][0]表示前i个删了j个后最大(和,颜色)

dp[i][j][1]表示颜色和最大严格不同(也就是和dp[i][j][0]的颜色不同)时次大的(和,颜色)

枚举转移即可,用一个值更新最大值,最大值没更新可以尝试更新次大值,

同时需要保证转移后,次大的颜色仍然不能和最大的颜色相同

暴力讨论倒是也可以,优雅一点的写法是和最大值比如果可更新就swap,

然后就可以用换下来的值和次大值比了

代码

// Problem: E - Colorful Subsequence
// Contest: AtCoder - Monoxer Programming Contest 2024(AtCoder Beginner Contest 345)
// URL: https://atcoder.jp/contests/abc345/tasks/abc345_e
// Memory Limit: 1024 MB
// Time Limit: 5000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=2e5+10,K=505;
const ll INF=1e16;
P dp[2][K][2];//dp[i][j][k]表示前i个删了j个后最大(和,颜色) 颜色和最大严格不同时次大的(和,颜色)
int n,k,c[N],v[N];
int main(){
	sci(n),sci(k);
	rep(i,1,n){
		sci(c[i]);
		sci(v[i]);
	}
	dp[0][0][0]=P(0,0);
	dp[0][0][1]=P(-1,0);
	rep(i,0,n-1){
		int f=i&1,g=f^1,up=min(i,k);
		//printf("i:%d f:%d g:%d up:%d\n",i,f,g,up);
		rep(j,0,up+1){
			dp[g][j][0]=P(-1,0);
			dp[g][j][1]=P(-1,0);
		}
		rep(j,0,up){
			rep(l,0,1){
				ll w=dp[f][j][l].fi;
				int col=dp[f][j][l].se;
				if(w==-1)continue;
				//printf("i:%d f:%d j:%d l:%d w:%lld col:%d\n",i,f,j,l,w,col);
				if(j+1<=k){//删,次数未超
					P cur=P(w,col);
					rep(x,0,1){
						if(x==1 && cur.se==dp[g][j+1][0].se)continue;//颜色严格不同
						if(cur.fi>=dp[g][j+1][x].fi){
							swap(dp[g][j+1][x],cur);
						}
					}
				}
				if(col!=c[i+1]){//不删,尾色不同
					P cur=P(w+v[i+1],c[i+1]);
					rep(x,0,1){
						if(x==1 && cur.se==dp[g][j][0].se)continue;//颜色严格不同
						if(cur.fi>=dp[g][j][x].fi){
							swap(dp[g][j][x],cur);
						}
					}
				}
			}
		}
	}
	ptlle(dp[n&1][k][0].fi);
	return 0;
}

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值