题意简述:
有n个机器,每个机器有2个芯片,每个芯片可以放k个电池。
每个芯片能量是k个电池的能量的最小值。
两个芯片的能量之差越小,这个机器就工作的越好。
现在有2nk个电池,已知它们的能量,我们要把它们放在n个机器上的芯片上,
使得所有机器的能量之差的最大值最小
自然,求解最大值最小问题应当用二分实现.主程序很容易就能写出来了
int n,k,m;
int arr[1000005];
int main (){
cin >> n>>k;
m = ((n * k) << 1);
for (int i = 1;i <= m;++ i){
scanf("%d",&arr[i]);
}
sort(arr + 1,arr + 1 + m);
int l = 0,r = arr[m] - arr[1];
while (l < r){
int mid = l + ((r - l) >> 1);
if (!canit(mid))
l = mid + 1;
else r = mid;
}
cout << l;
return 0;
}
重点就在于check
函数的实现
能否让每一组芯片差值都小于限定值t呢?
此前我们要先给电池分配制定一个最优策略
,这个策略的分配方式一定能使满足约束的机器尽量多
而进一步观察还可以发现:对于一颗芯片的k颗电池,只有最小的那颗电池可以决定一个方案的优度,我们称之为有效电池
,而剩下的k-1颗电池只不过是用于”凑数”的而已,我们称之为填充电池
.不同的两颗芯片交换填充电池
(假如它们比有用电池
大的话),对结果是没有影响的
那么问题可以解释为:
一个满足约束t的有用电池分配方案,是否有足够的填充电池使这个方案实现??
那么就来分配有用电池
一个有用电池
应当是一组k个电池中最小的,因此理应从小到大分配有用电池
而一组芯片的能量差最小,这组芯片的两颗有用电池
应当是相邻的(当然事先应该为电池按能量排序)
int j = 1;//为第j组芯片寻找有用电池
int cnt = 0;//记录当前所需的额外填充电池
for (int i = 1;i <= 2*n*k;++ i){
if (energy[i + 1] - energy[i] <= t)//假如这组电池满足约束,可以作为有用电池
++ j,cnt += 2 * (k - 1);//为这组芯片分配k-1个填充电池,并为下一组芯片寻找有用电池
else
cnt --;//假如这组电池不满足约束,那第i颗电池永远也没有机会成为有用电池了.但它还可以发光发热--为之前的有用电池充当填充电池(注意这些有用电池一定比第i颗电池能量小,因为它们已经按能量排序了).那么有了这位"志愿者",我们就可以省下一颗额外填充电池
if (j >= n) return 1;
}
注意:如果分配如上述那样顺利的话,这个方案就是成立的–一颗电池要么作为有用电池
,要么作为填充电池
,总之没有”永久废弃”的,而题目保证了有恰好2nk颗电池,如果每颗电池都被用到,那这些电池当然就会正好用完.所以这个方案就是成立的
那么反过来,方案不成立是什么情况?出现了”永久废弃”的电池
什么情况下第i颗电池连”别人的填充电池”都无法做到?如果此前的有用电池没有耗费任何一颗额外填充电池
,那么第i颗电池也就没办法去替代节省一颗额外填充电池
,那么它就被永久废弃了.
于是完整的check
函数如下(和上面那份代码实现方式不太一样,不过函数命名的意义是相同的)
bool canit(const int &t){
int cnt = 0,i = 1;
for (int j = 1;j <= n;++ j){
while (cnt >= 0 && arr[i + 1] - arr[i] > t) ++i,--cnt;
if (cnt < 0) return 0; //一旦没有额外填充电池,就说明出现了永久废弃电池,这种方案就不成立了
cnt += ((k - 1) << 1);
i += 2;
}
return 1;
}