招商银行2020FinTech精英训练营笔试题

我没参加,看了看题目,感觉很有意思,然后查了一下,三道原题,两道文字描述一毛一样。。。太不走心了。
但是题目本身质量我觉得不错,学习一下做法。

题目 交换座位

力扣765题情侣牵手
题目我就懒得写了。
大意就是N对情侣坐在同一排,交换几次位置可以使全部情侣挨着坐。
首先要记住一个结论,虽然我也不知道咋证明,就是假如有k对情侣混坐,交换k-1次就能让他们都挨着,那么没有坐错的就是1对情侣,交换0次。所以这是一个普适结论。
每个人编号为num,其中num/2相同的是一对。那么可以用并查集,假如在一个集合中有k个元素就表示这个集合里有k对情侣混坐。没有坐错的那对情侣他们的f[num/2] = num/2,集合中有一个元素。
我们知道交换次数等于集合元素个数减1,对所有集合的交换次数求和,集合元素求和为 N,1求和为集合个数,所以答案是N-集合个数,或者直接计算num/2 != find(num/2)的数量。
如果我直接做以我的智商怕是想不到并查集。。。真是太难了
既然想不到,那就记住它!

题目 金币

小招在玩一款游戏,在一个N层高的金字塔上,以金字塔顶为第一层,第i层有i个落点,每个落点有若干金币,在落点可以往左斜向下或右斜向下移动,问能获得的最大金币值。
其实也没啥好说的,就是动态规划数塔问题,从第N层塔往上走比较好算。
样例输入
5
8
3 8
8 1 0
4 7 5 4
3 5 2 6 5
输出
31

#include
#include
using namespace std;
int f[1100][1100];//f[i][j]从第i行第j个数到最后一行的最大数字和
int a[1100][1100];
int main()
{
    memset(a,0,sizeof(a));
    memset(f,0,sizeof(f));
    int n;
    cin>>n;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=i; j++)
            cin>>a[i][j];
    for(int i=n; i>=1; i--)
        for(int j=1; j<=i; j++)
            f[i][j]=a[i][j]+max(f[i+1][j],f[i+1][j+1]);
    cout<<f[1][1]<<endl;
    return 0;
}

题目 修塔游戏

小招在玩一款修塔游戏,系统中有n座高塔,每座高塔由若干个高度相同的方块堆砌而成,修塔的规则:
(1)每次从最高塔的塔尖拿走一个方块
(2)每次在最低塔的塔尖堆砌一个方块
小招一次只能进行两个动作中的一个,游戏目标是使n座塔中至少有k座高度相同,请问最少需要几次操作。
输入
第一行n和k,
第二行n个数表示塔高ai。
1<=k<=n<=200000
1<=ai<=10000
输出
最少操作次数
样例输入
6 5
1 2 2 4 2 3
输出
3

分析

这题本质上是有n个数,每次可以将最大值-1或者将最小值+1,至少几次操作可以使k个数相同。
菜鸡如我还是没啥思路。
然后我居然又找到原题了。。。是CodeForce上的题,原题描述就是n个数使k个相同,这里改成了修塔。。。
先排个序,然后考虑把塔修成ai的高度需要花费几步,为什么不需要考虑ai以外的高度呢?因为假如现在有两座塔高为3和5,那么其实修成3和修成4和修成5需要的操作数是一样的,不需要考虑4。
假如我们把从1到i的塔修成ai的高度,需要操作a[i]*i - sum[i]步数,其中sum[i]表示前缀和,假如i>=k,那么就是满足要求的,修k座塔的操作步数是a[i]*i-sum[i]-(i-k);同理如果我们把从i到n的塔都修成ai的高度,需要操作sum[n]-sum[i-1]-(n-i+1)*a[i]步数,如果n-i+1>=k就是满足 要求的,修k座塔的操作步数是sum[n]-sum[i-1]-(n-i+1)*a[i] - (n-i+1-k);同理如果要把1到n的塔都修成ai,需要i*a[i]-sum[i]+sum[n]-sum[i-1]-(n-i+1)*a[i],取k座则式子再减去n-k。
然后发现一个小细节没有注意到,如果本来就有k座塔高度相同呢,我们的操作目的是让n座塔变成ai-1,ai-1,ai,ai,ai,ai+1,ai+1这种形式,如果不需要操作呢?所以要先进行特判。
造了一组样例
输入
7 3
3 1 3 2 3 5 4
输出
0

代码

#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;
int k, n;
LL a[200010], sum[200010];
int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    sort(a+1, a+n+1);
    sum[0] = 0;
    int cnt = 1;
    for (int i = 1; i <= n; i++){
        sum[i] = sum[i-1] + a[i];
        if (i > 1){
            if (a[i]==a[i-1]) cnt++;
            else cnt = 1;
        }
        if (cnt >= k){
            cout << 0 << endl;
            return 0;
        }
    }
    LL ans = sum[n];
    for (int i = 1; i <= n; i++){
        if (i >= k) ans = min(ans, i*a[i]-sum[i]-(i-k));
        if (n-i+1>=k) ans = min(ans, sum[n]-sum[i-1]-(n-i+1)*a[i] - (n-i+1-k));
        ans = min(ans, i*a[i]-sum[i]+sum[n]-sum[i-1]-(n-i+1)*a[i] - (n-k));
    }
    cout << ans << endl;
    return 0;
}

写完博客以后突然觉得题目变得简单了!
进一步有一步的欢喜!

你可能感兴趣的:(C++笔试编程题)