尺取与分数规划

尺取

基本知识

形如:区间越大越容易符合条件/不符合条件。这样的问题,可以进行尺取。

对于一个 l l l ,找到第一个满足的 r r r ,显然 l , r l,r l,r 都是单调不减的。

例题练习1

例题1: n n n 个非负整数,求最小区间,使得区间和 > = S >=S >=S

分析: 越大越容易满足,对于每个 l l l ,找到第一个满足的 r r r

int ans=1e9,sum=0;
for(int i=1,j=0;i<=n;i++) {
	while(j<n&&sum<s)sum+=a[++j];
	if(sum<s)break; 
	ans=min(ans,j-i+1);
	sum-=a[i]; 
}

例题2: n n n 个数 a i a_i ai ,值域为 [ 1 , m ] [1,m] [1,m] 。求最小区间,使得 a i a_i ai [ 1 , m ] [1,m] [1,m] 中至少都出现一次。

分析: 同样是越大越容易满足,维护一个桶即可。

int ans=1e9,num=0;
for(int i=1,j=0;i<=n;i++){
	while(j<n&&num<m){
		count[a[++j]]++;
		if(count[a[j]]==1)num++;
	} 
	if(num<m)break; 
	ans=min(ans,j-i+1);
	count[a[i]]--;
	if(count[a[i]]==0)num--;
}

例题3: n n n 个数 a i a_i ai ,求 g c d > 1 gcd>1 gcd>1 的区间的最大长度。

分析: 越小越容易满足,每次找到一个最大且满足的即可。区间 g c d gcd gcd 可以用 s t st st 表快速维护。

例题4: n n n 个数 a i a_i ai ,求比 m i d mid mid 大的数, > = k >=k >=k 个,的区间个数

分析: 越大越容易满足,每次找到一个最小且满足的 r r r r ∈ [ r , n ] r\in[r,n] r[r,n] 均满足。

例题练习2:

例题1: n n n 个数, a i a_i ai ,求区间和的绝对值与 k k k 相差最小为多少,并输出这个区间。

分析:

  • 对前缀和排序,这样两个数的差值,就对应一个区间和。
  • 而且符合单调性,两个数越远,差值越大。
  • 每次找到第一个差值 > = k >=k >=k r r r,以及第一个差值 < = k <=k <=k r r r,即可。

例题2: 给定 n n n 个人的成绩 a i , b i a_i,b_i ai,bi ,每个人要么考到 a i a_i ai,要么考到 b i b_i bi 。定义及格为: > = >= >= n n n 个人中最高分的 p % p\% p% 。求最多多少人及格。

分析:

  • 将所有成绩放一起,从小到大排序。
  • 找到一个符合条件的区间,使得包含更多人的成绩。
  • 显然区间越大,越不容易满足题意,因此可以尺取。
  • 枚举 r r r ,左端点尺取到最后一个 l l l ,满足 v [ l ] > = v [ r ] × p % v[l]>=v[r]\times p\% v[l]>=v[r]×p% (并且 l , r l,r l,r 不属于同一个人成绩),然后检查 [ l , r ] [l,r] [l,r] 内有多少人的成绩即可。

分数规划

基本知识

模型:

形如: ∑ i = 1 n a i × w i ∑ i = 1 n b i × w i \frac{\sum_{i=1}^na_i \times w_i}{\sum_{i=1}^nb_i \times w_i} i=1nbi×wii=1nai×wi w i ∈ 0 , 1 w_i\in 0,1 wi0,1 。要求最大化/最小化该式子。

求解:

二分 m i d mid mid ,去 c h e c k check check ∑ i = 1 n a i × w i ∑ i = 1 n b i × w i > = m i d \frac{\sum_{i=1}^na_i \times w_i}{\sum_{i=1}^nb_i \times w_i}>=mid i=1nbi×wii=1nai×wi>=mid

令第 i i i 个数为 a i − m i d × b i a_i-mid\times b_i aimid×bi ,问题就变成选择若干个数,使得权值和 > = 0 >=0 >=0

例题练习

例题1: 限制取 k k k 个数,最大化式子。

分析: 显然取最大 k k k 个。

例题2: 限制分母 > = v >=v >=v ,最大化式子。

分析: b i b_i bi 看做重量, a i − m i d × b i a_i-mid\times b_i aimid×bi 看成价值,背包求 f [ V + ] f[V+] f[V+] 的最大值,即可。

int check(double mid){
	for(int j=1;j<=V;j++)f[j]=-1e9;
	for(int i=1;i<=n;i++){
		for(int j=V;j>=0;j--){
			int t=min(V,j+b[i]);//把f[V+]的最大值堆到f[i]身上
			f[t]=max(f[t],f[j]+a[i]-mid*b[i]);
		}
	}
	return f[V]>=0;
}

例题3: 给定 n n n 个点的一棵树,每个点有权值 a i , b i a_i,b_i ai,bi ,求所有环上 ∑ a i ∑ b i \frac{\sum a_i}{\sum b_i} biai 最大为多少。

分析:

  • 等价于 ∑ a i − b i × m i d > = 0 \sum a_i-b_i\times mid>=0 aibi×mid>=0 ,等价于 ∑ b i × m i d − a i < = 0 \sum b_i\times mid-a_i<=0 bi×midai<=0
  • b i × m i d − a i b_i\times mid-a_i bi×midai 当成点权,问题就变成:检查是否存在负环, s p f a spfa spfa 检查即可。
int check(double mid) {
	queue<int>q;
	for(int i=1;i<=n;i++){
		dis[i]=0;
		cnt[i]=vis[1]=1;
		q.push(i);//由于图不一定连通,将所有点入队
	}
	while(!q.empty()) {
		int u=q.front();
		vis[u]=0;
		for(int i=vex[u]; i; i=e[i].next) {
			int v=e[i].v;
			if(dis[v]>dis[u]+mid*b[u]-a[u]) {
				dis[v]=dis[u]+mid*b[u]-a[u];
				cnt[v]=cnt[u]+1;
				if(cnt[v]>n)return 1;
				if(!vis[v]) {
					vis[v]=1;
					q.push(v);
				}
			}
		}
		q.pop();
	}
	return 0;
}

例题4: 求区间最小平均值。

分析:

  • ∑ i = l r a i r − l + 1 = ∑ i = l r a i ∑ i = l r 1 \frac{\sum_{i=l}^r a_i}{r-l+1}=\frac{\sum_{i=l}^r a_i}{\sum_{i=l}^r 1} rl+1i=lrai=i=lr1i=lrai
  • 二分 m i d mid mid ,问题转化为: ∑ i = l r a i − m i d < = 0 \sum_{i=l}^r a_i-mid<=0 i=lraimid<=0 ,即最小字段和是否 < = 0 <=0 <=0

例题5: 求区间最大平均值。

分析: 同样的分析???不不不,区间最大平均值为区间最大值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值