2022-03-15每日刷题打卡

2022-03-15每日刷题打卡

代码源——每日一题

整齐的数组 - 题目 - Daimayuan Online Judge

Polycarp 有一个长度为 nn 的数组 a1,a2,…,an(n 是偶数)。Polycarp 还得到了一个正整数 k,他开始对数组 a 做如下操作:选择一个下标 i (1≤i≤n)i (1≤i≤n) 使 ai 减去 kk。

在 Polycarp 进行若干次操作后(可能 0 次),数组 a 中的所有数都变成相同的了。请你找到最大的符合要求的 k,如果 k 可以为任意大,请输出 −1。

输入格式

第一行一个整数 t,表示测试单元的个数。

接下来每个测试单元有两行。第一行包含一个偶数 n。第二行包含 n 个整数 a1,a2,…,an。

输出格式

对于每个测试单元输出单独一行一个整数 k (k≥1) —— Polycarp 能用来对数组进行操作的最大的数,或者 −1 —— 如果k 能任意大的话。

样例输入

3
6
1 5 3 1 1 5
8
-1 0 1 -1 0 1 -1 0
4
100 -1000 -1000 -1000

样例输出

2
1
1100

数据规模

所有数据保证 1≤t≤10,4≤n≤40(n 是偶数),−106≤ai≤106,并且 n 的总和不超过100。

这题简单来说,就是把其他数组的数一直减k,减到最小那个数,问这个数最大是多少。虽然题目说了要数组相同,但其实就是变成最小值的意思,比如最小值是x,其他值是y,那么题目的最终要求就是x-k*(次数)=y-k *(更多次数),即x=y-k *(更多次数)+k *(次数)。然后因为所有数都减的是同一个k,只是减的次数不同,这里我们就很容易想到是最大公因数。所以这题我们实际要求的是数组中其他值与最小值的差的最大公因数。两个数的最大公因数好求,但多个的怎么算呢?我们可以遍历一遍数组找最小的那个数,在算一趟其它数与这个最小数的差值,顺便算算有多少个数与最小值相同。然后我们把那些差值的因数全部求出来并存到哈希表里,如果有哪个因数的数量达到了n-最小值个数,说明这个因数是其它数与最小值的差值所共有的因数,我们在这类因数中取最大值即可。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<map>
#include<unordered_map>
#include<stack>
#include<queue>

typedef long long ll;
typedef pair<int, ll>PII;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
ll a[N], b[N], w[N], v[N], f[N];

int main() {
    int t;
    cin >> t;
    while (t--)
    {
        int n, res = 0, min_num = 1e9,k=-1;
        cin >> n;
        vector<int>v(n + 1);
        for (int i = 1; i <= n; i++)
        {
            cin >> v[i];
            min_num = min(min_num, v[i]);
        }
        map<int, int>mymap;
        for (int i = 1; i <= n; i++)
        {
            if (v[i] != min_num)
            {
                int ans = v[i] - min_num;
                for (int j = 1; j * j <= ans; j++)
                {
                    if (ans % j == 0)
                    {
                        mymap[j]++;
                        mymap[ans / j]++;
                    }
                }
            }
            else
            {
                res++;
            }
        }
        for (auto i : mymap)
        {
            if (i.second >= n - res)k = max(k, i.first);
        }
        cout << k << '\n';
    }
    return 0;
}
整齐的数组2 - 题目 - Daimayuan Online Judge

Polycarp 有一个长度为 n 的数组 a1,a2,…,an(n 是偶数)。Polycarp 还得到了一个正整数 k,他开始对数组 a 做如下操作:选择一个下标 i (1≤i≤n)使 ai 减去 k。

在 Polycarp 进行若干次操作后(可能 0 次),数组 a 中的数至少有一半都变成相同的了。请你找到最大的符合要求的 k,如果 k 可以为任意大,请输出 −1−1。

输入格式

第一行一个整数 t,表示测试单元的个数。

接下来每个测试单元有两行。第一行包含一个偶数 n。第二行包含 n 个整数 a1,a2,…,an。

输出格式

对于每个测试单元输出单独一行一个整数 k (k≥1)—— Polycarp 能用来对数组进行操作的最大的数,或者 −1 —— 如果 kk 能任意大的话。

样例输入

4
6
48 13 22 -15 16 35
8
-1 0 1 -1 0 1 -1 0
4
100 -1000 -1000 -1000
4
1 1 1 1

样例输出

13
2
-1
-1

数据规模

所有数据保证 1≤t≤10,4≤n≤40(n 是偶数),−106≤ai≤106,并且 n 的总和不超过100。

这题是上题的进阶,区别就是上一题要全部相等,这里只要有一半相等就行了。做法总体和上一题差不多,细节就有区别了。

上一题我们把数变成相等的问题转化成了数变成数组中最小数的问题,那么在这里也是一样的,我们还是取一个最小值然后把其它数变成这个最小值就可以了,但是题目现在说只要我们转一半的数组就可以了,那这样k的选择显然多了很多。我们从第1个数枚举到第n个数,“假设”这个数是最小的数,然后我们遍历数组,把所有大于它的数都记下来,同时也要记录下等于这个“最小值”的个数,如果这个“最小值”出现了n/2次,那说明k不管为什么,都至少有一半的数相等,直接输出-1。我们记下来的这些数+“最小值”的个数就组成了我们的数组,此时的问题就是,把这个数组的其他值都变成这个最小值,然后我们就用上面的方法写就行。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

typedef long long ll;
typedef pair<int, int>PII;

int main() {
    int t;
    cin >> t;
    while (t--)
    {
        int n, k = 0;
        cin >> n;
        vector<int>v(n + 1);

        bool flag = true;
        for (int i = 1; i <= n; i++)
            cin >> v[i];
        for (int i = 1; i <= n; i++)
        {
            map<int, int>mymap;
            if (!flag)break;
            int ans = 0,res=0;
            for (int j = 1; j <= n; j++)
            {
                if (v[j] > v[i])
                {
                    mymap[v[j] - v[i]]++;
                    res++;
                }
                else if (v[j] == v[i])
                {
                    ans++;
                }
                if (ans >= n / 2)
                {
                    cout << -1 << '\n';
                    flag = false;
                    break;
                }
            }
            if (res < n / 2 - ans)continue;
            map<int, int>gcd_num;
            for (auto j : mymap)
            {
                for (int l = 1; l * l <= j.first; l++)
                {
                    if (j.first % l == 0)
                    {
                        gcd_num[l] += j.second;
                        gcd_num[j.first / l] += j.second;
                    }
                }
            }
            for (auto j : gcd_num)
            {
                if (j.second + ans >= n / 2)k = max(k, j.first);
            }
        }
        if (flag)
        {
            cout << k << '\n';
        }
    }
    return 0;
}

力扣——每日一题

2044. 统计按位或能得到最大值的子集数目

给你一个整数数组 nums ,请你找出 nums 子集 按位或 可能得到的 最大值 ,并返回按位或能得到最大值的 不同非空子集的数目 。

如果数组 a 可以由数组 b 删除一些元素(或不删除)得到,则认为数组 a 是数组 b 的一个 子集 。如果选中的元素下标位置不一样,则认为两个子集 不同 。

对数组 a 执行 按位或 ,结果等于 a[0] OR a[1] OR … OR a[a.length - 1](下标从 0 开始)。

示例 1:

输入:nums = [3,1]
输出:2
解释:子集按位或能得到的最大值是 3 。有 2 个子集按位或可以得到 3 :

  • [3]
  • [3,1]

提示:

  • 1 <= nums.length <= 16
  • 1 <= nums[i] <= 105

暴力枚举,用类似01背包的方式来枚举所有可能的子数组排列,准备一个位数为数组长度+1的二进制数,然后开始枚举所有可能的子数组,对应位置的二进制数如果是1就把这个数加入子数组,0就不加,比如二进制数串此时为10101,就说明数组第一个第三个和第五个组成的子数组,我们用这些子数组来算它们按位或可能得到的最大值,并在此过程中记录最大值和最大值出现的次数。最后返回次数。

class Solution {
public:
    int countMaxOrSubsets(vector<int>& nums) {
        int n=nums.size(),max_num=0,up_num=1<<n,res=0;
        for(int i=0;i<up_num;i++)
        {
            int cnt=0;
            for(int j=0;j<n;j++)
            {
                if((i>>j)&1==1)
                {
                    cnt|=nums[j];
                }
            }
            if(cnt==max_num)
            {
                res++;
            }
            else if(cnt>max_num)
            {
                max_num=cnt;
                res=1;
            }
        }
        return res;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值