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;
}