0719-动态规划+ST表优化-CF 940E

本文介绍CF940E序列切割问题的解决思路,通过动态规划找到最佳序列切割方式,使得删除指定比例元素后的序列和最小。提供详细的算法分析及C++实现代码。

CF940E 序列切割

描述

给定一个长度为n的序列ai,和一个数字c。你需要将这个序列切成若干段,对于每一个长度为k的数字段,这段中最小的k/C个数字(向下取整)都会自动删除,问如何切割使得最后剩下的数字和最小,最小是多少?

如序列[3,1,6,5,2]当C=2,这是3+6+5=14

输入

第一行是2个整数N,C

第二行是N个整数Ai

输出

一个整数,最小值

样例输入

【样例输入1】
3 5
1 2 3
【样例输出1】
6
【样例输入2】
12 10 
1 1 10 10 10 10 10 10 9 10 10 10
【样例输出2】
92
【样例2解释】
其中一个最佳分区分别是[1,1],[10,10,10,10,10,10,10,10,10,10],其值分别为2和90。

提示

40%数据n,c<=2000

60%数据n,c<=10000

100%数据n,c<=100000,1<=ai<=10^9


分析:

若 n < c ,则不管怎么分都不会删去任何一个数

若 n == c ,则最好的情况就是删去一个最小值

若 c < n < 2 * c ,则原序列中最多出现一个完整的 c ,这意味着最多删去一个数

若 n >2 * c ,则每次都放完整的c为最佳答案,因为连续放在一起的c就会形成2 * c ,3 * c 等。

(虽然老师说这是很明显的动态规划,但是蒟蒻表示没有啊)

我们用 ans [ i ]表示现在选到 i 已经删除的最大值

那么对于每一个 i 而言,有两种情况

1.将它划分在长为c的序列中

2.不将它放在序列中(相当于是零散的)

对应这两种情况就可以得出状态转移方程了: ans[i]=max ( ans[i-c]+min { a[ k ] } , ans[i-1] );              ( k >= i - c + 1&&k <= i )

然后对于min { a [ k ] },我们可以用之前才讲的ST表进行优化


代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 100009
using namespace std;
int n,c;
long long sum=0,a[N],maxn=-1,ans[N];
long long dp[N][25];
int Log[N];
void init(){
	int i,j;
	Log[1]=0;
	for(i=2;i<=N;++i)
		Log[i]=Log[i/2]+1;//int-->向下取整 
}
void work(){
	int i,j,k;
	memset(dp,0x3f3f3f3f,sizeof(dp));
	for(i=1;i<=n;++i)	dp[i][0]=a[i];
	for(j=1;j<=Log[n];++j)
		for(i=1;i+(1<<j-1)<=n;++i)
			dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
}
int main(){
	scanf("%d%d",&n,&c);
	int i,j,k;
	long long minn=1e10;
	for(i=1;i<=n;++i)
	{
		scanf("%lld",&a[i]);
		sum+=a[i];
		minn=min(minn,a[i]);
	}		
	if(c==1) printf("0");
	else{
		if(n<c)	printf("%lld",sum);
		else{
			if(n==c) printf("%lld",sum-minn);
			else {
				init(); 
				work();
				for(i=c;i<=n;++i){
					int l=i-c+1,r=i;
					k=Log[r-l];
					minn=min(dp[l][k],dp[r-(1<<k)+1][k]);
					ans[i]=max(ans[i-c]+minn,ans[i-1]);
				}
				printf("%lld",sum-ans[n]);
				}
			}
		}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值