CF925B:贪心+二分

CF925B

题意:

  • 给出n个服务器的资源数,运行两个程序需要资源数为s1,s2
  • 选择k1个服务器,使资源数总和不小于s1,且每个服务器的资源数不小于s1/k1
  • 选择k2个服务器,使资源数总和不小于s2,且每个服务器的资源数不小于s2/k2
  • 选取的服务器不能有交叉

题解:

  • 贪心的思想,按服务器的资源数对服务器进行排序。
  • 然后枚举k1,找到满足资源数≥s1/k1的服务器的起始位置p,p+k1-1<=n,那么[p,p+k1-1]就为要选择的。
  • k1个为满足的最低条件了,所以我们就不用再考虑其它情况。把先前找到的k1个移动最后。把剩余的服务起都给s2,用同样的方法找是否有满足的。
  • 上面是先开始枚举k1。那么我们再来一次是先开始枚举k2。同样的思路。所以数组要复制一份。
  • 至于为什么这样?我也无法解释。可能这就是贪心。感觉对了就行。Orz!

代码:

#include <bits/stdc++.h>
using namespace std;
int const N = 300000 + 10;
int a1[N],a2[N];
int s1,s2,n,p,k1,k2;
struct Node
{
	int id,c;
	bool operator < (const Node& e) const{
		return c < e.c;
	}
}s[N],tmp[N];
int Move(int l,int r,int k){   //将区间[l,r]移到最后
	for(int i=r+1;i<n;i++)
		s[i-k]= s[i];
	return n - k;   //新的区间长度
}
bool solve(int &k,int &p,int a[N],int ss,int n){
	for(k=1;k<=n;k++){  //枚举k,服务器最低要求为ss/k
		p = lower_bound(s,s+n,(Node){0,(int)ceil(1.0*ss/k)}) -s ;
		if(p+k-1 < n){
			int cnt = 0;
			for(int i=p;i<=p+k-1;i++)	a[++cnt] = s[i].id;
				return true;
		}
	}	
	return false;
}
void Print(){
	printf("Yes\n");
	printf("%d %d\n",k1,k2);
	for(int i=1;i<=k1;i++)	printf("%d ",a1[i]);	printf("\n");
	for(int i=1;i<=k2;i++)	printf("%d ",a2[i]);	printf("\n");
}
int main(){
	scanf("%d%d%d",&n,&s1,&s2);
	for(int i=0;i<n;i++)
		scanf("%d",&s[i].c),	s[i].id = i+1;
	sort(s,s+n);
	bool flag = true;
	memcpy(tmp,s,sizeof(s));   //复制一份
	bool flag1 = solve(k1,p,a1,s1,n);    //先枚举k1
	int nn = Move(p,p+k1-1,k1);
	bool flag2 = solve(k2,p,a2,s2,nn);
	if(!flag1 || !flag2){
		memcpy(s,tmp,sizeof(tmp));     //复制回来,在枚举k2
		bool flag3 = solve(k2,p,a2,s2,n);
		int nn = Move(p,p+k2-1,k2);
		bool flag4 = solve(k1,p,a1,s1,nn);
		if(!flag3 || !flag4)	printf("No\n"),flag = false;
	}
	if(flag)	Print();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值