牛客练习赛 59 c 装备合成

链接:https://ac.nowcoder.com/acm/contest/4743/C
来源:牛客网

题目描述:

牛牛有x件材料a和y件材料b,用2件材料a和3件材料b可以合成一件装备,用4件材料a和1件材料b也可以合成一件装备。牛牛想要最大化合成的装备的数量,于是牛牛找来了你帮忙。

输入描述:

输入包含t组数据

第一行一个整数t

接下来t行每行两个整数x,y

输出描述:

每组数据输出一行一个整数表示答案。

朴素做法:

发现 三件甲和一件乙,共花费10件x, 10件b,所以可以通过这个去凑,最后朴素枚举当x、y不够10件的情况

#include <bits/stdc++.h>
using namespace std;
 
const int maxn = 1e5 + 5;
 
int a, b, t;
 
int main()
{
     scanf("%d", &t);
     while (t--)
     {
         scanf("%d%d", &a, &b);
         int ansa = 0, ansb = 0;
         if (a >= b)
         {
             int t = min(b, (a - b) / 3);
             b -= t, a -= t << 2;
            ansb = t + b / 10, ansa = b / 10 * 3;
             a -= b / 10 * 10, b %= 10;
         }
         else
         {
             int t = min(a >> 1, b - a);
             b -= 3 * t, a -= t << 1;
             ansb = a / 10, ansa = t + a / 10 * 3;
             b -= (a / 10) * 10, a %= 10;
         }
         if (a >= 4 && b > 0)
         {
             int t = min(a >> 2, b);
             ansb += t, a -= t << 2, b -= t;
         }
         if (a >= 2 && b >= 3)
         {
             int t = min(a >> 1, b / 3);
             ansa += t, a -= t << 1, b -= t * 3;
         }
         if (b == 0)
             while (ansa && a >= 6)
             {
                 --ansa, a += 2, b += 3;
                 int t = min(a >> 2, b);
                 ansb += t, a -= t << 2, b -= t;
             }
         else if (b)
             while (ansb && b >= 5)
             {
                 --ansb, b += 1, a += 4;
                 int t = min(a >> 1, b / 3);
                 ansa += t, a -= t << 1, b -= t * 3;
             }
         printf("%d", ansa + ansb);
         if (t) puts("");
     }
     return 0;
}

  

三分做法:

假设做n件甲,那么做$min(frac{x - 2 * n}{4},y - 3 * n)$件乙
总件数$all = n + min(frac{x - 2 * n}{4},y - 3 * n)$,求all的最大值
一般的就是把n枚举,但是从题目x,y<=1e9
n=[0,min(x/2,y/3)]——即完全不做甲 ->完全做甲,时间复杂度是1e9/2
所以我们要用三分缩小n的范围,然后在范围内枚举n
把all看出是关于自变量n的函数,$all=n+min(frac{x - 2 * n}{4},y - 3 * n)$
三分用之前一定要验证函数是$wedge$或$vee$或单增或单减的函数,即h(x)'在范围内最多一个零点
然而想一下,x、y差不多的时候($wedge$), x远远大于y的时候(单增),y远远大于x的时候(单减)
每次看两个三分之一点哪个更小,更小的更新为边界
 
#include <bits/stdc++.h>
using namespace std; 
const int maxn = 1e5 + 5; 

int a, b, t;
 
int is(int n)
{
     return n + min((a - 2 * n) >> 2, b - 3 * n);
}
 
int main()
{
     scanf("%d", &t);
     for (int i=0;i<t;i++)
     {
         scanf("%d%d", &a, &b);
         int l = 0, r = min(a / 2, b / 3);
         while (r - l > 3)
         {
             int m1 = l + (r - l) / 3, m2 = r - (r - l) / 3;
             if (is(m1) > is(m2)) r = m2;
             else l = m1;
         }
         int ans = 0;
         while (l <= r) ans = max(ans, is(l++));
         printf("%d
", ans);
     }
     return 0;
}