- 链接 :装备合成
- 题意 :
- 思路 :控制变量
设 方法一生成的装备数量为 m,则方法二生成的装备数量是
min((x - 2 * m)/4, y - 3* m)。
所以生成的总量函数 f(m) = m + min((x - 2 * m)/4, y - 3* m)。
m 的范围是 [0, min(x/2,y/3) ] ,即使用方法一生成的个数。而直接线性维护此函数的极值,复杂度最高是 1e9/2,所以需要用二分或者三分搜索极值,这就要观察此函数是递增(递减)或者凹凸函数。
(1)打表观察此函数
#include "bits/stdc++.h"
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define pb push_back
typedef long long ll;
const int maxn = 1e4 + 7;
const int INF = INT_MAX;
const double EPS = 10;
#define _DEBUG
int t, x, y;
int func(int mid){
return mid + min((x - 2 * mid)/4, y - 3* mid);
}
int main(){
#ifdef _DEBUG
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while (t--) {
cin>>x>>y;
for (int i = 0; i <= min(x/2,y/3); i++) {
cout<<func(i)<<endl;
}
}
return 0;
}
给定x = 1000, y = 1000,输出此函数的值
截取一部分:
可知此函数是凸函数。
(2)三分搜索
如果三分搜索不懂,可以看这个博客三分搜索
#include "bits/stdc++.h"
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define pb push_back
typedef long long ll;
const int maxn = 1e4 + 7;
const int INF = INT_MAX;
const double EPS = 10;
#define _DEBUG
int t, x, y;
int func(int mid){
return mid + min((x - 2 * mid)/4, y - 3* mid);
}
int main(){
#ifdef _DEBUG
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while (t--) {
cin>>x>>y;
int l = 0, r = min(x/2, y/3);
while (r - l > EPS) {
int mid1 = l + (r-l)/3;
int mid2 = r - (r-l)/3;
if (func(mid1) < func(mid2)) {
l = mid1;
}else{
r = mid2;
}
}
int ans = 0;
for (int i = l; i <= r; i++) {
ans = max(ans, func(i));
}
cout<<ans<<endl;
}
return 0;
}
- 遇到的问题:
(1)首先是这个函数没写出来,应该控制变量,设方法一生成的m件,再求方法二的。则这个函数自变量是m,三分搜索对m即可。
(2)EPS的设置。刚开始将EPS设置为1,复杂度超过了。
我们此题用到三分搜索的目的是,缩减区间,代码中设置r - l > 10,最后在l——r中维护一个max值即可。一般来说只有double类型的题目,才能精准确定极值。
具体视情况而定,我博客中其他几个三分搜索double类型的,EPS设置为1e-12,最后 l 即是驻点;而此题则缩减一定区间即可,EPS = 10遍很快乐。