2018.10.24【校内模拟】小 C 的数组(二分答案)(DP)

传送门


解析:

一看就是二分,然而这个DPDPDP我只会O(n3)O(n^3)O(n3)的区间DpDpDp做法。。。
一看觉得可做,考场上就去推式子去了,结果推挂了。。。
一看题解,均分的思想。。。我去我第一个想的就是这个正解,结果被我莫名其妙的否了啊啊啊啊?!!!!

思路:

其实考虑如果只允许改变一个怎么选择,直接选择差最大的一组中的一个,调整成均分就行了。

比如我们现在有一组数据1,5,13,191,5,13,191,5,13,19,只允许调整一个,那么我们肯定选择555131313中的一个,调整至这个数的两边均分,显然这里将555调整成777是最优的。

那么现在这里要求调整多个数,怎么办?

显然如果我们能够调整相邻数之差到不超过xxx,那么必然存在方案不超过x+1x+1x+1,即解的存在性是随xxx单调的。考虑二分。

那么怎么验证,考虑上述的均分的思想,如果原数列中abs(ai−aj)&lt;=(j−i)×xabs(a_i-a_j)&lt;=(j-i)\times xabs(aiaj)<=(ji)×x,那么我们可以通过调整i&lt;k&lt;ji &lt; k &lt; ji<k<j的所有aka_kak来使得i−ji-jij这段区间满足条件,即调整j−i−1j-i-1ji1个数。

那么怎么求出需要调整的最少个数?考虑DpDpDp

我们用fif_ifi表示保持iii不变,令区间i−ni-nin满足性质需要的最少调整次数。
那么状态转移就很好想了fi=min{n−i,min{fj+j−i−1}(abs(ai−aj)&lt;=(j−i)×x)}f_i=min\{n-i,min\{f_j+j-i-1\}(abs(a_i-a_j)&lt;=(j-i)\times x)\}fi=min{ni,min{fj+ji1}(abs(aiaj)<=(ji)×x)}

那么对于每个二分出的xxx,这样DPDPDP一下,再判断是否能够满足不超过kkk次就行了。

代码中的checkcheckcheck用了一个贪心的小剪枝,显然我们可以通过把前i−1i-1i1个数调整与aia_iai相等,使得满足xxx的限制,这时候就会调整fi+i−1f_i+i-1fi+i1个数,贪心判断一下就好了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	re bool f=0;
	while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return f?-num:num;
}

cs int N=2003;
int n,K;
int a[N];
int f[N];

inline bool check(int x){
	for(int re i=n;i;--i){
		f[i]=n-i;
		for(int re j=i+1;j<=n;++j){
			if(abs(a[j]-a[i])<=1ll*x*(j-i))f[i]=min(f[i],f[j]+j-i-1);
		}
		if(f[i]+i-1<=K)return true;
	}
	return false;
}

int maxn;
signed main(){
	n=getint();K=getint();
	for(int re i=1;i<=n;++i){
		a[i]=getint();
		maxn=max(maxn,abs(a[i]-a[i-1]));
	}
	int l=0,r=maxn;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	cout<<l;
	return 0;
}

转载于:https://www.cnblogs.com/zxyoi/p/10047168.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值