2020.4.26腾讯暑期实习生招聘笔试题

题目有点多,但不算难,好几道属于裸题。

题目一

Q哥在玩一个游戏,他面对一共n个怪物,击杀第i个怪物需要花费ci的血量,可获得wi的金币,在游戏开始Q哥可以花费金币来购买血量,那么游戏结束Q哥最多可以赚多少金币(我的理解:相当于求纯利润)
输入
第一行两个数字n和m,表示怪物数量和每个金币能够购买的血量。
接下来n行,每行两个数字ci和wi。
1<=n<=1000
1<=wi<=100
1<=m<=1e9
1<=ci<=1e6
输出
一个数字表示Q哥最大收益
样例一
输入
3 2
1 1
1 10
3 1
输出
10
开局花一块买2点血,击杀1和2获得11个金币,去掉成本净赚10。
样例二
输入
1 2
3 1
输出
0
亏本买卖选择不做。

分析

笔试的时候没想出来,看评论区学习的做法。
以为是背包然后一直纠结这个m太大了,其实压根不需要背包,这里可以购买的血量本来就没有上限,本质上就是尽可能把赚到钱的怪物都击杀了,如果击杀了能赚到钱的怪物以后还有血量多,就可以把本来不赚钱的怪物杀了,把这题联想到买卖上来就很容易思考了。
首先根据wi/ci的值排序,显然该值越大越赚钱。然后模拟一下买卖的过程,更新最大收益。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;

struct Node
{
    int cost, val;
    double p;
}guai[1010];
int n, m;
bool cmp(const Node& a, const Node& b)
{
    return a.p > b.p;
}
int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i++){
        cin >> guai[i].cost >> guai[i].val;
        guai[i].p = 1.0*guai[i].val / guai[i].cost;
    }
    int ans = 0;
    sort(guai, guai+n, cmp);
    int blood = 0;
    int buy = 0;
    int sum = 0;
    for (int i = 0; i < n; i++){
        if (blood < guai[i].cost){
            buy = (guai[i].cost - blood) / m;
            if ((guai[i].cost - blood)%m) buy++;
            blood = blood + m*buy - guai[i].cost;
        }
        else {
            buy = 0;
            blood -= guai[i].cost;
        }
        sum += guai[i].val;
        sum -= buy;
        ans = max(ans, sum);
    }
    cout << ans << endl;
    return 0;
}

题目二

求抛物线y^2 = 2Ax与直线y=Bx+C所围成的封闭图形面积,若图形不存在,则输出0。
在这里插入图片描述
输入
第一行一个T,表示测试组数;
接下来三个正整数A,B,C;
1<=T<=1000
1<=A,B<=100
-100<=C<=100
输出
一个浮点数,允许误差在1e-4范围内。
样例
输入
1
1 1 -6
输出
31.2481110540

分析

经典的定积分题目,相信考过研的同学肯定都很熟悉,既然正着看比较难受(y^2=2Ax不是一个一一映射),那就把图翻过来,以y轴为水平轴,就是个很明显的两个函数(一一映射)围成的区域。
在这里插入图片描述
y1和y2用求根公式计算。
在这里插入图片描述

这里A,B,C都是正整数,所以没有围成图形的情况就是A<=2BC

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

int A, B, C;
double get(double y)
{
    return y*y/2/B - C*y/B - y*y*y/6/A;
}

int main()
{
    int T; cin >> T;
    while (T--){
        cin >> A >> B >> C;
        if (A-2*B*C <= 0){
            cout << 0 << endl;
            continue;
        }
        double y1 = (1.0*A-1.0*sqrt(A*A-2*A*B*C)) / B;
        double y2 = (1.0*A+1.0*sqrt(A*A-2*A*B*C)) / B;
        double s = get(y2)-get(y1);
        printf("%.5f\n", s);
    }
    return 0;
}

题目三

现在在一个监狱里,有n个房间连在一排,编号从1到n,每一个房间内有一个人。
现在要每个房间的人选一个1到m之间的数字,若相邻房间选择的数字一样则会发生冲突。
请问有多少种情况下有可能发生冲突。
输入
第一行两个整数m和n,表示数字范围和房间数量。
1<=n<=1012
1<=m<=108
输出
输出方案数量,答案对100003取余。
样例
输入
2 3
输出
6

分析

数学的排列组合问题,显然求不发生冲突的方案数更加方便,第一个人有m种选法,第二个人有m-1种,第三个第四个到第n个人都有m-1种选法,方案数为m*(m-1)(n-1),总的方案数有mn种,减一下就是答案。
但是n很大,所以必须用快速幂。快速幂其实很容易理解,就是看二进制下当前位是否为1,如果是就把结果乘以当前的base。
有一个需要注意的点,在进行减法的时候,因为减完还要进行取余,而负数是无法取余的,所以要在减完以后加上100003

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

typedef long long LL;
const LL mod = 100003;
LL n, m;

int main()
{
    cin >> m >> n;
    LL num1 = 1;
    LL tmp = m;
    LL cnt = n;
    while (cnt)
    {
        if (cnt&1) num1 = num1*tmp%mod;
        cnt >>= 1;
        tmp = tmp*tmp%mod;
    }
    LL num2 = 1;
    tmp = m-1;
    cnt = n-1;
     while (cnt)
    {
        if (cnt&1) num2 = num2*tmp%mod;
        cnt >>= 1;
        tmp = tmp*tmp%mod;
    }
    num2 = num2 * m % mod;
    LL ans = (num1- num2 + mod) % mod;
    cout << ans << endl;
    return 0;
}

题目四

有n个物品,每个物品有k个属性,第i件物品的第j个属性用一个正整数表示为ai,j,两个不同的物品i,j被称为完美对当且仅当ai,1+aj,1 = ai,2+aj,2 = ai,k+aj,k,求完美对的个数。
输入
第一行n,k。
接下来n行,第i行k个数字表示ai,1,ai,2,…,ai,k
1<=n<=105,2<=k<=10,1<=ai<=100
输出
一个数字表示答案
样例
输入
5 3
2 11 21
19 10 1
20 11 1
6 15 24
18 27 36
输出
3

分析

这题没时间想了,暴力拿了30%
看评论区的思路是把ai,1,ai,2-ai,1,…,ai,k-ai,1作为匹配特征,找这个特征的相反数组成的特征的数量。

题目五

现在有107个用户,编号1~107,已知m对关系,每一对关系给你两个数x,y,代表编号为x的用户和编号为y的用户在一个圈子中,假如A和B在一个圈子中,B和C在一个圈子中,那么ABC就在一个圈子中,想知道最多的一个圈子有多少用户。
输入
第一行一个T,表示T组测试数据
每一组测试数据,第一行一个n表示n对关系
接下来n行每一行两个数x和y,表示关系
1<=T<=10
1<=n<=105
1<=x,y<=10^7
输出
对于每一组数据,输出圈子内最多人数

分析

并查集裸题,107有点大,但是因为n只有10^5所以可以离散化一下,但是我在写的时候图快(做不完了要)只记录了一下最大最小编号来稍微优化,居然过了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;

int f[10000010];
int find(int x)
{
    if(f[x]!=x)
        f[x]=find(f[x]);
    return f[x];
}
void work(int a,int b)
{
    int tx = find(a);
    int ty = find(b);
    f[ty] = tx;
    return ;
}
int a[100010], b[100010];
int main()
{
    int T; cin >> T;
    int n, minx = 10000001, maxx = 0;
    map<int, int> mp;
    while (T--){
        cin >> n;
        mp.clear();
        for (int i = 1; i <= n; i++){
            cin >> a[i] >> b[i];
            minx = min(minx, a[i]);
            minx = min(minx, b[i]);
            maxx = max(maxx, a[i]);
            maxx = max(maxx, b[i]);
        }
        for (int i = minx; i <= maxx; i++)
            f[i] = i;
        for (int i = 1; i <= n; i++)
            work(a[i], b[i]);
        int ans = 0;
        for (int i = minx; i <= maxx; i++){
            int father = find(i);
            mp[father]++;
            ans = max(ans, mp[father]);
        }
        cout << ans << endl;
    }
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值