题目链接
链接:https://ac.nowcoder.com/acm/contest/18907/N
来源:牛客网
题目描述
牛牛有{x}x件材料{a}a和{y}y件材料{b}b,用{2}2件材料{a}a和{3}3件材料{b}b可以合成一件装备,用{4}4件材料{a}a和{1}1件材料{b}b也可以合成一件装备。牛牛想要最大化合成的装备的数量,于是牛牛找来了你帮忙。
解析
使用三分的使用一定要注意要么这是个二次函数,要么这是个分段的凹凸函数,千万不能把两条斜率不同但作用域重合了的直线合并出来的函数盲目的看成一个凹凸函数
本题其实也可以对极值点的两边分别二分求最优解
证明
设:以2a,3b方式合成的装备有m件 以4a,b方式合成的装备有n件
m取值范围为 0 ~ min(x/2,y/3),
合成的装备总数就是
F(m) = m + min((x-2,)/4,y-3m);
为分段函数,拆掉min得到
F(m) = (x+2m)/4, m <= (4y -x)/10;
F(m) = y - 2m ,m > (4y - x)/10;
函数F(m)中x,y为常数,所以F(m)关于m从左到右先增后减,为凸函数,所以可以对函数F(m)使用三分
三分算法使用技巧
这里有一个三分的技巧,考虑到整数除法的小数丢失,所以你最后得到的是近似点,可能不是真正的极值点,但它一定非常靠近极值点,这时你只要枚举一下近似点的附近的那几个点就行
code
#include<bits/stdc++.h>
using namespace std;
int main(){
int T; cin >> T;
while(T--){
int x,y; cin >> x >> y;
int l,r,m1,m2;
l = 0; r = min(x/2,y/3);
while(l < r){
m1 = l + (r - l)/3;
m2 = r - (r - l)/3;
int ans1 = m1 + min( (x - 2 * m1)/4, y - m1 * 3);
int ans2 = m2 + min( (x - 2 * m2)/4, y - m2 * 3);
if(ans1 >= ans2)
r = m2 - 1;
else l = m1 + 1;
}
int ans = 0;
int ma = min(x/2,y/3);
for(int i = max(0,l - 3) ; i <= min(l + 3,ma); ++i)//枚举近似点范围的所有点
ans = max(ans,i + min( (x - i * 2)/4, y - i * 3));
cout << ans << endl;
}
return 0;
}