牛客小白月赛32. C & E

C.消减整数

题目:

给出一个正整数H,从1开始减,第一次必须减1,每次减的数字都必须和上一次相同或者是上一次的两倍,请问最少需要几次能把H恰好减到0。
输入描述:
第一行给出一个正整数T,1 ≤ T ≤ 10^4,接下来T行每行一个H, 1 ≤ H ≤ 10^9。
输出描述:
每行一个正整数代表最少的次数

输入
3
3
5
7

输出
2
3
3

题解:利用贪心的思想,肯定希望最后一个减去的数字2^n越大越好,并且要求出最少次数,那么我们更希望他是1、2、 2^2、 2^3……之和(换言之也就是正好是一个等比数列之和,该等比数列最后一个数为 2^n)。
等比数列之和:s = 2^(n+1) - 1

  1. 若s = x,那cnt = n + 1
  2. 若s > x,此时2^(n+1) - 1 < s < 2^(n+2) - 1,那么s先缩小(2^(n+1) - 1) newS = s - 2^(n+1) - 1,此时的cnt1 = (n + 1),而再想求newS最少需要由几个2^x 组成,那可以直接遍历它的二进制表示,看有几个“1”即可(因为newS 肯定小于2^n ,所以肯定可以由2^0 -> 2^(n-1)组成 ),这就是cnt2的次数,最后结果cnt1 - 1 + cnt2(因为此时的cnt1 = n + 2才结束)。

代码:

#include<iostream>
using namespace std;
int t, n;

int main()
{
    cin >> t;
    while(t --)
    {
        int cnt1 = 0, cnt2 = 0;
        cin >> n;
        for(int i = 1; ; i++)
        {
            if(n <= (1 << i) - 1)
            {
                cnt1 = i;
                break;
            }
        }
        if(n == (1 << cnt1) - 1) cout << cnt1 << endl;
        else
        {
            n -= ((1 << (cnt1 - 1)) - 1);
            if(n & 1) cnt2 ++;
            while(n)
            {
                n >>= 1;
                if(n & 1) cnt2 ++;
            }
            cout << cnt1 - 1 + cnt2 << endl;
        }
    }
    return 0;
}

E. 春游

题目:

盼望着,盼望着,东风来了,春天脚步近了。

值此大好春光,老师组织了同学们出去划船,划船项目收费如下:

双人船最多坐两人,也可以坐一人,收费a元

三人船最多坐三人,也可以坐两人或者一人,收费b元

本次出游加上带队老师共n人,如何安排能使得花费最小呢?

输入描述:
第一行给出一个正整数 T(1 ≤ T ≤ 1000),代表测试数据的组数。

接下来T行每行给出三个正整数n, a, b, 1 ≤ n , a , b ≤10^9 ,含义如题。
输出描述:
每组输入输出一行,代表最小的花费
输入
2
2 20 200
3 20 20

输出
20
20

题解:分类讨论 + 贪心思想。
实际上我认为这道题倾向两个原则,首先就是每条船的每个位置的平均价值越小,就尽可能都选它,但最后的位置安排又尽可能的满足不浪费每个位置,因为每个位置自身都带有自己的价值。以下分五种情况进行讨论:

  1. 全部坐两人船(此时人数可能刚好坐满/多出一人):val = (n + 1)/ 2 * a;
  2. 全部坐三人船(此时人数可能刚好坐满/多出一人/多出两人):val = (n + 2)/ 3 * b;
  3. 多出一人时,主要坐两人船(多出的一人找一条两人船的人凑一条三人船,就算不凑那条三人船一样被浪费):val = (n - 2) / 2 * a + b(?)
  4. 多出一人时主要坐三人船(选一条三人船的人与多出的一人进行组合,凑成2条两人船):val = (n - 2) / 3 * b + 2 * a(?)
  5. 多出一人/两人时主要坐三人船(选三人船一人与他凑一条双人船/直接一条双人船)val = n / 3 * b + a

不过感觉上面的分类讨论有点难想,另一种解法就是先判断两种船每个座位的平均票价: a/2 与 b/3

  1. a/2 < b/3(尽可能坐两人船)
    1. 两人船正好坐满
    2. 多出一人,这一人有两种坐法,一种是自己坐一条最便宜的船,或者从两人船中挑出两人,这三人一起坐一条三人船
  2. a / 2 >= b / 3(尽可能坐三人船)
    1. 三人船刚好坐满
    2. 多出一人,这一人有两种坐法,一种是自己坐一条最便宜的船,或者从三人船中挑出三人,这四人一起坐两条两人船
    3. 多出两人,这两人有两种坐法,要么一条双人船,要么一条三人船

代码:

#include<iostream>
using namespace std;
typedef long long ll;
int t;
ll n, a, b;
int main()
{
    cin >> t;
    while(t --)
    {
        ll res;
        cin >> n >> a >> b;
        if(a / 2 < b / 3)
        {
            res = n / 2 * a;
            if(n & 1) res = min(res + min(a, b), res - a + b);
        }
        else
        {
            res = n / 3 * b;
            if(n % 3 == 1) res = min(res + min(a, b), res - b + 2 * a);
            else if(n % 3 == 2) res += min(a, b);
        }
        cout << res << endl;
    }
    return 0;
}

posted @ 2021-03-26 19:51  ~K2MnO4  阅读(347)  评论(0)    收藏  举报