笔试题

目录

英特尔(20.9.11)

简单计算器

链表排序

将空格替换为Tab

携程(20.9.8)

敏感词替换

枚举所有路径

思科(20.9.7)

跳跃游戏

搜狗(20.9.5)

兑奖

建房子的方案数

百度(20.9.3)

分配角色

走台阶

华为(20.9.2)

分两种糖果

湖泊数量

经典0-1背包

Shopee(20.9.2)

相邻石子合并

构造具有循环节的字符串

OPPO(20.08.29)

第k大的斜率

同余方程的最小正整数解

亚马逊(20.08.27)

完全二叉树的层次遍历转为中序遍历

腾讯(20.8.23)

第k小子串

拆分数字,使得数位之和最大

刷木板的最少次数

一个字符串最少可拆成几个回文子串

(20.04.26)非降序的卡牌

猿辅导(20.08.22)

逆时针遍历完全二叉树

环状矩阵的最大子矩阵和

猜数字游戏

网易互联网(20.08.08)

最短回文串

平分物品

排队买票

互相认可的人数

拼多多(20.08.02)

飞行棋

午餐和晚餐的最少摄入热量

网易雷火(20.08.02)

翻转完全二叉树的子树

商品装包

打字的最少按键次数

讯飞(20.07.31)

兑钱问题

输出排序的每趟结果

矩形重叠

浪潮(20.07.20)

搬石头排序

翻转0/1串

招商银行Fintech(20.04.29)

让k个数相等

情侣交换座位

网易互娱 (20.04.11)

支持删除操作的并查集

错排序列的最小加权距离

微软(20.03.25)

分组使每组内部极差最小

删除回文子数组


  • 英特尔(20.9.11)

简单计算器

实现一个基本的计算器来计算一个简单的字符串表达式的值。

字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -。

只有个位数的加减。可能有负数,比如-1+2。

保证输入字符串没有语法错误。没有空格和其他符号。

和这题差不多:224. 基本计算器

 

链表排序

原题:148. 排序链表

 

将空格替换为Tab

给一个字符串,尽可能多地将空格转为Tab,输出转换后的字符串。

Tab会按4个字符的宽度对齐。

比如"1...2" (点表示空格) 可以转为"1\t2"。

输入:"1..2"    输出:"1..2"
输入:"1...2"   输出:"1\t2"
输入:"1....2"  输出:"1\t.2"

 

  • 携程(20.9.8)

敏感词替换

输入三行,第一行是敏感单词 a,第二行是一个句子,第三行单词 b。把句子中的所有 单词a 替换成 单词b。注意忽略敏感词内部顺序,即如果敏感词是you,那么ouy也应该被替换。

输入:
you
i love you,uoy love me
jack

输出:
i love jack,jack love me

【思路】暴力遍历句子,每次取子串(和敏感词等长)和敏感词比较,通过对子串排序判断是否为敏感词,如果是就替换掉。AC 44%

#include 
#include 
#include 
using namespace std;
string mingan, line, rep, ans;

bool equa(string a, string b){
    sort(a.begin(), a.end());
    sort(b.begin(), b.end());
    return a==b;
}

int main(){
    getline(cin, mingan);
    getline(cin, line);
    getline(cin, rep);
    int wordlen = mingan.size(), linelen = line.size();

    ans = "";
    for(int i=0; i

 

枚举所有路径

给一个字符串数组,数组长度不固定。每个字符串代表一个节点,每个节点可以取对应字符串中的任意一个字符。所有节点的字符连起来就是一条路径。输出所有可能的路径(按样例中的顺序)

比如 a bc da ef,一条路径为abde。

如果构造出来的路径字符串中有重复字符,就在末尾加上"--circular dependency"

输入:
a bc da ef

输出:
abde
abdf
abae--circular dependency
abaf--circular dependency
acde
acdf
acae--circular dependency
acaf--circular dependency

【思路】DFS。因为节点数(即数组长度)不固定,所以通过循环枚举不可行,因为不知道要嵌套多少层循环。而DFS可以解决这个问题。 AC 88%

#include 
#include 
#include 
#include 
#include 
using namespace std;
vector v;
unordered_map cnt;
int n;

// id:第几个节点, ans:当前路径
void dfs(int id, string ans){
    if(id==n){  // 递归边界
        cnt.clear();
        for(int i=0;i1){
                ans += "--circular dependency";
                break;
            }
        }
        cout<

 

  • 思科(20.9.7)

跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

【思路】力扣原题,但本题输入是字符串,需要用 istringstream 和 getline 来实现类似 split 的作用。

 
#include 
#include 
#include 
using namespace std;
vector v;

bool f(){
    int n=v.size();
    int rm = 0;
    for(int i=0;i=n-1)
                return true;
        }
    }
    return false;
}

int main(){
    int n;
    string s,t;
    cin>>s;
    istringstream is(s);
    getline(is, t, '[');

    while(getline(is, t, ',')){
        int n = stoi(t);
        //cout<

 

  • 搜狗(20.9.5)

三题的题解+代码:https://blog.csdn.net/nimphy/article/details/108424445

 

兑奖

你有三种道具A,B,C,分别有a个,b个,c个.

用A,B,C各一个,可以兑换1件奖品.
为了尽可能多的兑换奖品,你可以把其中的任意2个道具(包括2个同类的道具)换成一个任意指定道具,比如一个A和一个B可以兑换一个C,两个A也可以兑换一个C。
求最多可以兑换多少件奖品。

输入:4,4,2, 代表a,b,c
输出:3,最多兑换奖品数

输入:9,3,3
输出:4
解释:先兑换3件奖品,还剩[6,0,0],用A分别换成一个B和一个C,即[2,1,1],可以再兑换一件奖品

【思路】

先排序,使得a

首先直接合成a件奖品,这时a变成0

ans = 0
ans += a
a -= a      // a = 0
b -= a
c -= a

然后再判断 c-2*b > b。如果是,就把B先兑完,最后兑C。方法是先拿2b个C合成A道具,然后合成b个奖品。这时b变成0,只剩c可能大于0,然后每5个C 可以合成一个奖品(2个用于合成一个B,2个用于合成一个A)

ans += b
c -= 3*b //先花2*b个合成b个A,然后合成b个奖品,合计3b个
b = 0
ans += c / 5

否则,把一部分C换成A使得B,C的数量相等,并直接把A兑完。然后每2个b、2个c可以兑换一个奖品。

temp = (c-b)/2;//先合成temp个奖品
ans += temp;
c = c - temp * 3;
b -= temp;
ans += (b+c) / 4;

完整代码参考:https://blog.csdn.net/nimphy/article/details/108424445

 

注:也有用【二分法】的:

https://www.nowcoder.com/discuss/500256

https://www.nowcoder.com/discuss/500320

 

建房子的方案数

题意:在一维的X轴上现有n个房子,给出每个房子的中心和长度(比如[1,3]代表房子的区间为[-0.5, 2.5]),是按顺序排列的,并且没有重叠的房子。问建造一栋长度为 t 的且至少紧挨着一个房子的方案有多少种。

输入:[0,2,5,3], t=2
输出:4
解释:第一个房子位于[-1,1],第二个房子位于[3.5, 6.5],中间的间距为2.5 > t,所以可以挨着第一个房子、第二个房子分别建,有4种方案

【思路】只需要判断每两个相邻房子之间能否建房子,如果房子之间的长度大于 t 那么就有2种方案,等于 t 就只有一种方案,否则就没有。

怎么比较小数:把所有输入的数都乘10就没有小数了。

 

  • 百度(20.9.3)

分配角色

有n个人面试一部戏,这个戏有m个角色。每个角色都有一个价值b[ j ],每个人要求自己演的角色价值至少为a[ i ],求一种可行的方案,要求能够演戏的人数最多,输出每个人分配的角色号。如果一个人没有合适的角色,输出-1。

如果有多种方案满足要求,任意输出一种即可。

输入:
T:样例数
n,m:表示人数、角色数
数组a:n个数,表示每个人要求的最低价值
数组b:m个数,表示每个角色的价值

输出:
n个数,表示每个人的角色号

【思路】贪心。将a、b数组分别按升序排序,每个人a[i] 依次选择满足自己要求的价值最小的角色a[j]。

但由于排序会丧失原始下标,所以a和b都要用结构体保存原始下标。

#include 
#include 
#include 
using namespace std;
int v[1005];    // 第i人分到的角色号,0表示没分到角色
bool sel[1005]; // 第i个角色是否已被分配

struct stu{
    int num,index;  // 每个人要求的最低价值和自己的原下标
}a[1005],b[1005];

bool cmp(stu x,stu y){
    if(x.num==y.num) return x.index>T;
    while(T--){
        memset(v,0,sizeof(v));
        memset(sel,0,sizeof(sel));
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++) {cin>>a[i].num; a[i].index=i;}
        for(int i=1;i<=m;i++) {cin>>b[i].num; b[i].index=i;}
        sort(a+1,a+n+1,cmp);
        sort(b+1,b+m+1,cmp);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(a[i].num<=b[j].num && sel[j]==false){
                    v[a[i].index] = b[j].index;
                    sel[j]=true;
                    break;
                }
            }
        }

        for(int i=1;i<=n;i++){
            if(i>1) cout<<" ";
            if(v[i]>0) cout<

 

走台阶

有n个台阶(n<100000),一个人每次最多走m步(m<=7),但他有个习惯,每个走的步数不能和前2步相同。问他有多少种方案走完台阶。

输入:7 3
输出:2
解释:2种方案为[1,2,3,1], [1,3,2,1]。像[1,2,1,3]、[2,2,3]这种都不行

【思路】DFS。用一个vector数组保存前面走过的步数,这样可以知道前2步的步数。

【结果】超时,通过40%

#include 
#include 
using namespace std;
vector step;
int cnt = 0;
int n,m;

void dfs(int rest){
    if(rest==0){
        cnt++;
        return;
    }
    int n=step.size();
    for(int i=1;i<=m;i++){
        if(n>=1 && (step[n-1]==i)) continue;
        if(n>=2 && (step[n-2]==i)) continue;
        if(rest>=i){
            step.push_back(i);
            dfs(rest-i);
            step.pop_back();
        }
        else break;
   }
}

int main(){
    cnt = 0;
    step.reserve(100001);
    step.clear();
    cin>>n>>m;
    dfs(n);
    cout<

 

  • 华为(20.9.2)

分两种糖果

【思路】用一个结构体保存每个人的索引和糖果数量, 两种糖果对应两个结构体的数组,然后每个数组分别排序(按糖果数量降序),比较两个数组的前3个人的糖果总数谁更大。AC 70%

#include 
#include 
#include 
using namespace std;

struct Stu{
    int num, index;
};
vector a1,a2; //2种糖果

bool cmp(Stu& x,Stu& y){
    if(x.num==y.num) return x.indexy.num;
}

int main(){
    int n, clas;
    cin>>n;
    Stu t;
    for(int i=0;i>t.num>>clas;
        t.index = i+1;
        if(clas==1) a1.push_back(t);
        else a2.push_back(t);
    }
    sort(a1.begin(),a1.end(),cmp);
    sort(a2.begin(),a2.end(),cmp);
    int sum1 = a1[0].num+ a1[1].num+ a1[2].num;
    int sum2 = a2[0].num+ a2[1].num+ a2[2].num;
    if(sum1>sum2){
        cout<

 

湖泊数量

【原题】岛屿数量

 

经典0-1背包

卡车空间为K,有N个箱子要装入卡车,每个箱子价值为v[i],体积为w[i]。求能装入卡车的最大总价值。

注:输入的所有数均<1000。

【思路】DP。AC 80%

#include 
#include 
using namespace std;
int w[1005],v[1005], dp[1005];

int main(){
    int k, n;
    cin>>k>>n;
    for(int i=1;i<=n;i++)
        cin>>w[i];
    for(int i=1;i<=n;i++)
        cin>>v[i];

    memset(dp, 0, sizeof(dp));
    for(int i=1;i<=n;i++){
        for(int j=k; j>=0; j--){
            if(j>=w[i])
                dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
            else dp[j]=dp[j];
            if(j==0) continue;
        }

    }
    cout<

 

  • Shopee(20.9.2)

相邻石子合并

设有N堆石子排成一排,其编号为1,2,3,…,N。每堆石子有一定的质量,可以用一个整数来描述,现在要将这N堆石子合并成为一堆。

每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

例如有4堆石子分别为 1 3 5 2, 我们可以先合并1、2堆,代价为4,得到4 5 2, 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24;

如果第二步是先合并2,3堆,则代价为7,得到4 7,最后一次合并代价为11,总代价为4+7+11=22。

输出最小代价。

输入:[1 3 5 2]
输出:22

原题:

https://blog.csdn.net/ttomchy/article/details/105160473

https://www.cnblogs.com/stul/p/10333281.html

 

构造具有循环节的字符串

给定一个字符串s,在s后面至少加几个字符可以让它具有循环节。

输入
aaa
abca
abcde

输出
0
2
5

【原题】HDU 3746

【题解】https://blog.csdn.net/u013480600/article/details/22954037

 

  • OPPO(20.08.29)

第k大的斜率

【题目】给n个点的坐标,问两两连线中第k大的斜率是多少(取整数)

原题:https://www.luogu.com.cn/problem/P4479

 

同余方程的最小正整数解

【题目】给定a和b,求ax ≡ 1%b的最小正整数解x

【思路】扩展欧几里得算法的应用:https://blog.csdn.net/agoniangel/article/details/51169179

int exgcd(int a,int b,int &x, int &y){
    int r,t;
    if(!b){
        x=1;
        y=0;
        return a;
    }
    r=exgcd(b,a%b,x,y);
    t=x;
    x=y;
    y=t-a/b*y;
    return r;
}

int main(){
    int a,b;
    while(cin>>a>>b){
        int x,y;
        int g = exgcd(a,b,x,y);
        cout<<(x%b+b)%b<

 

  • 亚马逊(20.08.27)

完全二叉树的层次遍历转为中序遍历

【题目】给定一个数组,代表一颗完全二叉树的层次遍历结果。a[i]的子节点为a[2i+1]和a[2i+2]。

比如[1,2,9,5,-1,7,6],对应的二叉树如下(-1忽略)

    1
   / \
  2   9
 /   / \
5   7   6

 计算出中序遍历结果[5,2,1,7,9,6],输出中序数组中每个元素的左右两个数之和,即[2,6,9,10,13,9]

【思路】只要将层次遍历数组转为中序遍历数组就好做了。因为层次遍历数组我们知道父子结点的位置关系,所以可以按照中序遍历的递归写法求中序遍历数组:

vector v;

void inorder(int i){               // 初始:i=0
    if(i>=n) return;
    if(2*i+1

然后把数组中的-1去掉,再计算一下 每个元素的左右两个数之和就可以了。

 

  • 腾讯(20.8.23)

第k小子串

【样例解释】aabb所有子串按字典序依次为:a, aa, aab, aabb, ab, abb, b, bb (重复字符串不计入,比如2个a只算一个)

【思路】用堆(优先队列)保存子串,使得最小的子串永远在堆顶。遍历子串时,先将所有单个字符作为一个子串入堆,然后每次取出堆顶的子串,并将这个子串和它的后一个字符连起来作为一个新的子串入堆。

参考:https://blog.csdn.net/qq_43142794/article/details/99542217 (这题要把重复字符串计入,但总体思路一样)

#include
#include
#include
#include
#include
using namespace std;
char a[5005];
int k;

struct _string{
    string s;    // 子串
    int pos;     // 子串末尾字符的下标

    // 优先队列默认为大顶堆, 重载<号使得字典序最小的子串在队头
    bool operator <(const _string &x)const{
        if(x.s==s)return pos>x.pos;
        return s>x.s;
    }
}x;

int main(){
    priority_queue<_string> q;
    vector v;
    scanf("%s",a+1);
    scanf("%d",&k);
    int len=strlen(a+1);
    for(int i=1;i<=len;i++){
        x.s="";
        x.s+=a[i];
        x.pos=i;
        q.push(x);
    }
    while(!q.empty()){
        x=q.top();
        q.pop();

        if(v.empty() || v.back()!=x.s){    // 判断是否和前一个子串重复,不重复才加入
            v.push_back(x.s);
            k--;
        }
        if(!k)break;

        if(x.pos

 

拆分数字,使得数位之和最大

输入:
1
35
输出:
17
说明:35=19+16,val=1+9+1+6=17。35=29+6也可以。

 

刷木板的最少次数

输入
5
2 2 1 2 1
输出
3

输入
2
2 2
输出
2

【思路】分治(递归)。先从最下面横着刷, 当出现断层时,这些木板就被分成多个连续的部分,对每个部分递归刷。

最后取横着刷的次数和竖着刷的次数(就是木板个数)的较小者。

【参考】https://blog.csdn.net/u013582254/article/details/40157725

const int maxn=5500;
int h[maxn],n;
int solve(int l,int r)
{
    int len=r-l+1;

    // 最下面minn层横着刷,需要刷minn次
    int minn=h[l];
    for(int i=l;i<=r;i++)
        minn = min(minn, h[i]);
    for(int i=l;i<=r;i++)
        h[i] -= minn;
    int ans = minn;

    for(int i=l;i<=r;i++)
    {
        if(h[i])
        {
            int ll=i;
            int k=i+1;
            while(h[k]&&k<=r)
                k++;
            int rr=k-1;
            ans+=solve(ll,rr);
            // 为什么i不直接跳到rr+1: 
            // 因为solve(ll,rr)使得这个区间全为0(刷完了),执行if(h[i])会自动跳过
            // 直接跳到rr+1也行
        }
    }
    ans = min(ans, len);    // len是竖刷的次数
    return ans;
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&h[i]);
        printf("%d\n",solve(1,n));
    }
    return 0;
}

 

一个字符串最少可拆成几个回文子串

 

(20.04.26)非降序的卡牌

有n张卡牌, 每张卡牌正面值是 ai ,反面值是 bi 。每次可以任意选择相邻的2张卡牌,交换位置,然后都翻转。求得到非降序的卡牌的最小操作次数。如果无解,返回-1。

输入:第一行n(n<=18),表示卡牌数量。下面两行,每行有n个数,分别表示a[i]和b[i]。
输出:最小操作次数

样例输入
3
1 3 2
3 2 1

样例输出
1

【思路】冒泡排序的思想。

int a[20],b[20];
int fun(int n){
    int ans=0;
    for(int i=0;ia[j+1] && b[j]>b[j+1]){
            ans++;
            swap(a[j], b[j+1]);
            swap(b[j], a[j+1]);
        }
    }
    for(int i=1;ia[i])
            return -1;
    return ans;
}

int main(){
    int n;
    cin>>n;
    for(int i=0;i>a[i];
    for(int i=0;i>b[i];

    cout<
  • 猿辅导(20.08.22)

逆时针遍历完全二叉树

【代码】https://www.nowcoder.com/discuss/485790

 

环状矩阵的最大子矩阵和

 ​

【代码】https://www.nowcoder.com/discuss/485790

 

猜数字游戏

输入:3 0
输出:2
解释:先猜数字2,若答案是2,则获胜。否则需要花费2金币。如果返回猜大了,只可能是1,下次必胜。如果返回猜小了,同理必胜。

输入:3 1
输出:0
解释:先猜数字2,并使用1次不花金币的特权。若答案是2,则获胜。如果返回猜大了,只可能是1,下次必胜。如果返回猜小了,同理必胜。

【代码】https://www.nowcoder.com/discuss/485790

 

  • 网易互联网(20.08.08)

最短回文串

原题:LeetCode 最短回文串,唯一不同是一个在前面插入字符,一个在末尾插入字符,思路一样。

 

平分物品

笔试题_第1张图片

【思路】DFS。每件物品有3个选择:给第1个人、给第2个人、扔掉。在递归过程中判断 两个人当前拿到的价值是否相等,若相等就更新minLoss(最少需要扔的价值)

#include 
using namespace std;
int a[20], suffixSum[20];    // 后缀和(a[i]到a[n-1]的和)
int n, minLoss;

void dfs(int first, int second, int cost, int index){
    // 若所有物品都分配完(或被扔掉),应退出递归
    if (index >= n){
        if (first == second)    // 若此时两人拿到的价值相等,说明这是一种平分方案
            minLoss = min(minLoss, cost);
        return;
    }

    // 若当前两人拿到的价值相等,则已经是一种平分方案(后面的物品全部扔掉)
    if (first == second)
        minLoss = min(minLoss, suffixSum[index] + cost);

    // (剪枝)若两个人的价值差比后面所有物品总价值还大,说明已经不可能平分,提前返回
    if (abs(first - second) > suffixSum[index])
        return;

    dfs(first + a[index], second, cost, index + 1);    // 给第1个人
    dfs(first, second + a[index], cost, index + 1);    // 给第2个人
    dfs(first, second, cost + a[index], index + 1);    // 扔掉
}

int main(){
    int t;
    cin >> t;
    while (t--){
        scanf("%d", &n);
        for (int i = 0; i < n; ++i)
            scanf("%d", &a[i]);
        memset(suffixSum, 0, sizeof(suffixSum));
        for (int i = n - 1; i >= 0; --i)
            suffixSum[i] = suffixSum[i + 1] + a[i];
        minLoss = INT32_MAX;
        dfs(0, 0, 0, 0);
        cout << minLoss << endl;
    }
    return 0;
}

 

排队买票

一个售票员早上8点上班,有n个排队买票,给两个数组a和b,a[i]表示第 i 个人买票需要a[i]秒,b[i]表示第 i 个人和第 i+1 个人一起买票需要b[i]秒。问售票员最早几点下班。

笔试题_第2张图片

【思路】动态规划,dp[i]表示前 i 个人买票最少需要的时间

dp[i] = min(dp[i-1]+a[i], dp[i-2]+b[i-1])

#include 
#include 
using namespace std;
int a[2006], b[2006], dp[2006];

string int2str(int a){    // 小于10的在前面补0
    if(a>=10) return to_string(a);
    return "0" + to_string(a);
}

int main(){
    int T;
    cin>>T;
    while(T--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i>b[i];

        dp[1] = a[1];
        for(int i=2;i<=n;i++){
            dp[i] = min(dp[i-1]+a[i], dp[i-2]+b[i-1]);
        }

        // 将dp[n]转换成时/分/秒
        int _sec = dp[n]%60;
        int _minu = (dp[n]/60)%60;
        int _hour = dp[n]/60/60;
        _hour+=8;

        if(_hour>12){
            _hour -= 12;
            cout<

 

互相认可的人数

笔试题_第3张图片

 

  • 拼多多(20.08.02)

飞行棋

给定k和n,其中k代表在下飞行棋时距离终点还有k步,n代表接下来走了n步。

同时给定n个数,a[i]表示第i步走的步数。(1<=a[i]<=6)

输出走完n步后距离终点的距离、回退次数。如果在前n-1步中就到达了终点,不用输出这2个数,而是输出"paradox"。

(回退:比如现在离终点3步,但摇骰子要走5步,那到终点时要回退2步,即离终点还有2步)

【思路】简单模拟,注意k等于0的情况。

#include 
using namespace std;

int main(){
    int k,n, a[105];
    cin>>k>>n;
    int cnt=0;
    for(int i=0;i>a[i];
    if(k==0) {
        cout<<"paradox"<a[i]) k-=a[i];
        else if(k==a[i]) {
            k-=a[i];
            break;
        }
        else {
            cnt++;
            k = a[i]-k;
        }
    }

    if(k==0 && i

 

午餐和晚餐的最少摄入热量

午餐有n种套餐,晚餐有m种套餐。每种套餐有一个美味度deli [i]、一个热量值hot[i]。午餐和晚餐都最多只能吃一种套餐,也可以不吃。求在两餐美味度之和至少达到 t 的情况下,摄入的最少热量。若无论怎么选都不能使美味度之和达到 t,则输出-1。

输入:
早餐套餐数n, 晚餐套餐数m, 最少美味度t
n行:每行2个数,分别为该早餐套餐的美味度、热量值
m行:每行2个数,分别为该晚餐套餐的美味度、热量值

输出:最少摄入热量

【思路】暴力求解。午餐和晚餐的套餐分别按照美味值从高到低排序,美味值相等,热量少的在前。然后遍历每一种午餐和晚餐的搭配,求满足美味度之和>= t 的最少热量。时间复杂度O(n*m) 。

只吃一餐和两餐都不吃的情况单独考虑。

#include 
#include 
#include 
using namespace std;
struct point{
    int hot;
    int deli;
}a[100005], b[100005];

bool cmp(point& a, point& b){   // 按美味度从高到低排序
    return (a.deli>b.deli || (a.deli==b.deli && a.hot<=b.hot));
}

int main(){
    int n,m,t;
    cin>>n>>m>>t;
    int maxa = -1, maxb = -1;
    for(int i=0;i>a[i].hot>>a[i].deli;
        if(a[i].deli>maxa) maxa = a[i].deli;
    }
    for(int i=0;i>b[i].hot>>b[i].deli;
        if(b[i].deli>maxb) maxb = b[i].deli;
    }
    if(maxa+maxb=t){
        int i=0;
        while(a[i].deli>=t) {minhot=min(minhot, a[i].hot); i++;}
    }
    if(maxb>=t){
        int i=0;
        while(b[i].deli>=t) {minhot=min(minhot, b[i].hot); i++;}
    }
    if(minhot!=INT_MAX){
        cout<=t){
                minhot = min(minhot, a[i].hot + b[j].hot);
            }else break;
        }
    }

    cout<

 

  • 网易雷火(20.08.02)

翻转完全二叉树的子树

给定N,表示完全二叉树层数,按层序遍历的顺序给节点编号:1,2,3,...,2^n-1​. 

给定m,交换编号为m的节点的左右子树(子树的左右子树保持原状)。

按层序遍历的顺序输出翻转后的完全二叉树。

【思路】开一个数组保存按层序遍历的结果,初始时a[i]=i。完全二叉树的性质:节点 i 的左右子节点分别为 2i 和 2i+1。

首先 swap(a[2*m], a[2*m+1]),然后分别把2*m和2*m+1的子节点编号填到对应位置即可。

#include 
using namespace std;
int a[10000], n;

void dfs(int i){
    if(2*i+1 <= n){
        a[2*i] = 2*a[i];
        a[2*i+1] = 2*a[i]+1;
        dfs(2*i);
        dfs(2*i+1);
    }
}

int main(){
    int N, m;
    cin>>N>>m;
    n = (1<

 

商品装包

有n种商品,第 i 种商品有 a[i] 件。现在要把它们装包,每个包要装 k个商品 (1<=k<=n) ,并且k个商品种类互不相同,问最多可以装多少个包。

输入
n=5 k=3
[1,2,3,4,5]

输出:5

【思路】贪心。先把数组 a 按升序排序,每次选最多的k种商品装包, 然后把剩下的商品重新排序。重复这个过程,直到已经没有k种商品(倒数第k个数等于0)。

#include 
#include 
using namespace std;
long long a[1005];

int main(){
    int n,k;
    cin>>n>>k;
    for(int i=0;i>a[i];
    
    long long ans = 0;
    sort(a,a+n);
    while(n-k>=0 && a[n-k]>0){
        ans++; //ans += a[n-k]; // 不应该一次性减完
        for(int i=n-1; i>=n-k; i--)
            a[i]--; //a[i] -= a[n-k];  // 不应该一次性减完
        sort(a,a+n);
    }
    
    cout<< ans <

 

打字的最少按键次数

某人为完成任务,至少要打n个字,问最少需要按键多少次。他可以有以下操作:

  • 打1个字,需要按键1次
  • 全选(ctrl+A),需要按键2次
  • 复制选中的文本(ctrl+C),需要按键2次。执行完复制后,选中的文本仍然处于选中状态。
  • 粘贴(ctrl+V),需要按键2次。如果粘贴前有选中的文本,则粘贴会替换掉选中的文本。
  • 取消选中(Esc),需要按键1次
输入:100
输出:29

【思路】动态规划。dp[i] 表示至少打 i 个字的最少按键次数。有2种情况:

(1)已经打出 i-1 个字,这时只需要再打1个字。

(2)已经打出 j 个字(2<= j =n。注:这里要按 i 能否被 j 整除分两种情况,不能整除时要多粘贴1次。

dp[i] 等于 以上两种情况的较小值。

#include 
#include 
using namespace std;
int dp[1005];

int main(){
    int n;
    cin>>n;
    
    dp[1] = 1;
    for(int i=2;i<=n;i++){
        dp[i] = dp[i-1]+1;
        
        for(int j=i-1; j>=2; j--){
            if(i%j==0){
                //dp[i] = min(dp[i], dp[i/j]+5+2*(j-1));
                dp[i] = min(dp[i], dp[j]+5+2*(i/j-1));
            }else{
                //dp[i] = min(dp[i], dp[i/j]+5+2*(j-1)+2);
                dp[i] = min(dp[i], dp[j]+5+2*(i/j-1)+2);
            }
        }
    }
    cout<

 

  • 讯飞(20.07.31)

兑钱问题

假设1元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5张。现在要用这些钱来支付 k 元,至少要用多少张纸币?如果没有解决方案,则返回 -1。

【思路】贪心算法,每次都取面值最大的纸币/

【代码】https://blog.csdn.net/qq_39539470/article/details/79720035 

 

输出排序的每趟结果

用某种排序方法对关键字序列(例如 25,84,21,47,15,27,68,35,20)进行排序,序列的变化情况采样如下:

输入
9
25 84 21 47 15 27 68 35 20

输出
15 20 21 25 47 27 68 35 84
15 20 21 25 47 27 68 35 84
15 20 21 25 47 27 68 35 84
15 20 21 25 35 27 47 68 84
15 20 21 25 27 35 47 68 84
15 20 21 25 27 35 47 68 84

请模拟这个过程,输出排序的每趟结果(即使每趟排序后的序列没有变化也需要输出)。

【思路】有点像快排,但又不是。以下是按快排思想编写的代码,过了60%数据。

#include 
using namespace std;

int a[100010];
int n;

void print(){
    cout<pivot)
            r--;
        //swap(a[l], a[r]);
        while(l>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    qsort(1,n);
}

 

矩形重叠

判断两个矩形是否存在重叠(只有点或边重叠时也算).若存在返回1,否则返回0.

一个矩形以左下角和右上角坐标  [x1, y1, x2, y2] 的形式给出。

输入:0,0,2,2,1,1,3,3
输出:1

注:题目并未保证两个顶点的顺序,也未保证两个矩形的顺序。

【思路】本题和LeetCode 836. 矩形重叠基本相同,不同的是:只有点或边重叠的情况,在本题中算重叠,而LeetCode不算。

 
#include 
using namespace std;

int main(){
    int r1[4], r2[4];
    for(int i=0;i<4;i++) cin>>r1[i];
    for(int i=0;i<4;i++) cin>>r2[i];
    
    // 保证左下角坐标在前
    if(r1[0] >= r1[2] || r1[1] >= r1[3]){
        swap(r1[0], r1[2]);
        swap(r1[1], r1[3]);
    }
    if(r2[0] >= r2[2] || r2[1] >= r2[3]){
        swap(r2[0], r2[2]);
        swap(r2[1], r2[3]);
    }

    // 对于LeetCode,只有点或边重叠的矩形不算重叠,只需要去掉下面的=号
    if((r1[0]<=r2[2] && r1[1]<=r2[3] && r1[2]>=r2[0] && r1[3]>=r2[1]) ||
       (r2[0]<=r1[2] && r2[1]<=r1[3] && r2[2]>=r1[0] && r2[3]>=r1[1]))
        cout<<1<

 

  • 浪潮(20.07.20)

搬石头排序

沙滩摆放着一排大小不一的球形石头,已知第i个石头的半径为ri,不存在两个石头半径相等。现要求通过移动石头使摆放的石头从左往右半径递增。每次可选择一块石头,并把它放在剩下n-1块石头的最左边或最右边。求最少操作次数。
输入:第一行一个整数n,表示石头个数。(1 <= n <= 100000).第二行n个整数,表示从左往右石头的半径r1,r2,…,rn( 1<= ri <= n)。保证不存在两个不同的石头半径相等。
输出:最少操作次数。

输入:
6
3 2 1 4 6 5
输出:
3

【思路】本题可以转化为求最长递增子序列的长度。比如3 2 1 4 6 5,排完序后:1 2 3 4 5 6,对比两个字符串,发现原字符串3 4 5排完序相对位置没变,所以3 4 5即为原字符串的最长递增子序列。剩下有多少个其他字符就应该挪动多少次。故最少的操作数 = n-最长递增子序列的长度

【代码】

https://blog.csdn.net/guojunxiu/article/details/107735085 O(nlogn),通过

https://blog.csdn.net/JRNSWYJH/article/details/102640563 O(n^2),超时

 

翻转0/1串

给出一长度为n的01串,现在可以将其中任意一个子串翻转(0变1,1变0),问翻转后的串中最长的01相间的子序列的长度(子序列可以不连续)

Input
第一行为一个整数n表示序列长度,第二行一个长度为n的01串
Output
翻转后最长01相间子序列的长度

输入
8
10000011
输出
5

【原题】CodeForces 603 A

【题解】显然翻转一个子串不会改变这个子串中01相间串的串长,而翻转一个子串之所以能够增加整个串01相间串的串长是因为将翻转子串端点处相同的数字经过翻转变成不同的数字了,所以只需找整个串中有多少相邻两个数字相同即为增量,但是注意增量最多为2

#include 
using namespace std;

int main(){
    int n;
    cin>>n;
    string s;
    cin>>s;
    int cnt1=0, cnt2=0;
    for(int i=0;i

https://www.it610.com/article/5620327.htm

https://www.cnblogs.com/zzuli2sjy/p/5016070.html

 

  • 招商银行Fintech(20.04.29)

让k个数相等

【链接:牛客、Codeforces Round #629 (Div. 3) F】

给定n个数和k,可以执行两种操作:(1)把最大数减1,(2)把最小数加1。

求最少需要多少次操作,使n个数中至少有k个数相等。

输入:输入共有2行,第一行为n和k(1≤k≤n≤200000 ),第二行为n个数 a1, a2, ...an(1≤aj≤10000)。
输出:最少操作次数

样例输入
7 5
3 3 2 1 1 1 3
样例输出:4

样例输入
11 3
1 2 3 4 5 5 5 6 7 8 9
样例输出:0

样例输入
11 4
1 2 3 4 5 5 5 6 7 8 9
样例输出:6

【思路】贪心。一个不完全对的思路(牛客AC,但Codeforces未通过样例1)。

将数组排序,先判断一下数组中是否有k个数相等,有的话直接返回0。

没有的话,我们把使得操作次数最少的这k个相等数的值称为基准数。

基准数一定在排序后的前k个数或后k个数中,否则如果在中间,操作次数会更大。

计算一下把前k-1个数都加到第k个数的操作次数cnt1,和把后k-1个数都减到倒数第k个数的操作次数cnt2,那么最少的操作次数应该是min(cnt1,cnt2)。(这么算其实不对,因为基准数不一定恰好是第k个数或倒数第k个数,参考样例1)

代码:

#include 
using namespace std;
int nums[200005];

int fun(int n, int k){
    sort(nums,nums+n);

    // 检查是否本来就有k个数相等,有则不需要任何操作
    int max_dup = 1, now_dup = 1;       // 重复最多的次数、当前数重复的次数
    for(int i=1; i= k) return 0;
        }
    }

    int cnt1 = 0, cnt2 = 0;
    for(int i=0; i=0 && nums[index2--]==nums[n-k]) cnt2--;
    return min(cnt1, cnt2);
}

int main(){
    int n,k;
    cin>>n>>k;
    for(int i=0; i>nums[i];
    cout<

另一个思路是,对于前k个数和后k个数中的每个数,计算它作为基准数所需要的操作次数,然后返回最少的操作次数。(待实现)

 

情侣交换座位

【链接:牛客、LeetCode 765】

N 对情侣坐在 2N 个座位上,但他们并未按顺序就座。 计算最少交换座位的次数,以便每对情侣可以坐在一起。一次交换可选择任意两人,让他们站起来交换座位。

人和座位用 0 到 2N-1 的整数表示,情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2N-2, 2N-1)。这些情侣的初始座位  row[i] 是由最初始坐在第 i 个座位上的人决定的。

【思路】贪心.每2个座位视为一个沙发,固定每个沙发的第一个人,若第二个人不是他的情侣,就把第二人换成它的情侣. 
时间O(n^2),空间O(1)

 

  • 网易互娱 (20.04.11)

支持删除操作的并查集

n个数(1-n)初始时各自独立构成一个集合。可以这些数进行三种操作:

(1)将X和Y所属集合进行合并。如果本来就在同一集合中,则什么也不做。

(2)将X从其所属集合中删去,并独立构成一个新集合。如果X本来就独立构成集合,则什么也不做。

(3)输出X所属集合的元素个数。

输入:第一行T,代表样例数。每个样例的第一行输入两个数n和m,n代表元素个数,m代表操作数(n\leq 10000,m\leq 1000)。接下来m行,每行第一个数为操作数,1表示执行操作(1),以此类推。如果操作数是1,则后面输入两个数X和Y,否则输入一个数X。

输出:每执行一次操作(3),输出当前X所属集合的元素个数。

思路

常规的并查集不能实现删除操作,因为每个元素的父节点都是数组中的数,删除一个父节点会把集合中所有数都删掉。因此需要改一下父节点的设置方法。

常规并查集的初始化方法是把每个元素的父节点初始化成自己,fa[u]==u,u这个节点就是所在集合的父节点,此时,u就有了两重身份:既是一个元素,又代表一个集合(是这个集合的父节点)。而删除操作的关键就是只改变这一个元素的父节点,而不能改变集合中其它元素的父节点。因此要把每个元素的两重身份分开,u只表示元素,任何元素不能作为集合的父节点,做法是把每个元素的父节点设置为虚拟的节点。这样删除一个元素也不会影响其它元素,因为它不可能是父节点。

#include 
using namespace std;

const int N = 21005;
int father[N], id;  // id是下一个新集合的父节点

void init(int n){   // 初始化父节点
    id = n+1;
    for(int i=1;i<=n;i++)
        father[i] = id++;   // i的父节点不再是i,而是i+n
    for(int i=n+1;i<=2*n;i++)
        father[i] = i;
}

int getFather(int i){
    return i==father[i] ? i : (father[i]=getFather(father[i]));
}

void Union(int x,int y){
    int fa1 = getFather(x), fa2 = getFather(y);
    if(fa1 != fa2)
        father[fa2] = fa1;
}

void Remove(int i){
    father[i] = id;
    father[id] = id;
    id++;
}

int main(){
    int T;
    cin>>T;
    while(T--){
        int n,m;
        cin>>n>>m;
        init(n);
        while(m--){
            int op,x,y;
            cin>>op;
            if(op==1){  // 合并x,y所在集合
                cin>>x>>y;
                Union(x,y);
            }
            else if(op==2){    // 独立出x
                cin>>x;
                Remove(x);

            }
            else{  // 输出x所在集合大小
                cin>>x;
                int root = getFather(x);
                int cnt = 0;
                for(int i=1;i<=n;i++)
                    if(getFather(i)==root)
                        cnt++;
                cout<

 

错排序列的最小加权距离

一个数组的错排序列就是所有元素都不在原来的位置上。给定一个数组a和权重数组v,在a的所有错排序列中,求a和错排序列的最小加权距离。

序列X和错排序列Y的加权距离定义:W(X,Y)=\sum v_i*|i-i_Y|,其中|i-i_Y|表示X[i]在两个序列中位置的距离。

输入:第一行T,代表样例数。每个样例的第一行输入一个数n(2\leq n\leq 20),第二行为数组a的n个数,第三行为这n个数的权重。

输入:数组a和错排序列的最小加权距离。

输入

2
3
1 2 3
1 2 3

4
4 3 2 1
1 1 1 1

 

输出

7

4

思路: 首先这题和数组a没有任何关系,影响加权距离的是权重,和元素值无关。

(1)如果n是偶数,那么每两个相邻元素进行交换就是最优解,这样交换得到的一定是错排的。任意一个元素交换前后的距离都是1,所以最小加权距离 = sum(v)

(2)如果n是奇数,那么两两交换就会多出一个数,所以有三个相邻元素要进行交换,其它元素依然是每两个相邻元素进行交换。

那么怎么选这三个元素呢?很容易想到选择权重最小的数周围的三个数。

当权重最小的数位于奇数位时,比如1 2 3这三个权重,权重最小的数应该交换得最远,这样才能使距离最小。所以交换成2 3 1就是最小距离。

但是当权重最小的数位于偶数位时,比如2 1 3,最小的权重1无论和谁交换,距离都是1,权重2和3必有一个交换距离为2,那肯定最优解是把权重次小的2交换得最远,即交换成1 3 2得到最小距离。

综上,我们可以发现,n是奇数时,应该选择奇数位上权重最小的数进行距离为2的交换(即与 i+2 或 i-2 位置进行交换),这时其它n-1个元素都可以进行距离为1的交换,所以最小加权距离 = sum(v) + 奇数位最小的权重

int main(){
    int T, a[30], v[30];
    cin>>T;
    while(T--){
        int n;
        cin>>n;
        for(int i=0;i>a[i];
        for(int i=0;i>v[i];

        int sum = accumulate(v,v+n,0);
        if(n%2==0) cout<

 

微软(20.03.25)

题解:https://www.cnblogs.com/dockerchen/p/12578881.html

 

分组使每组内部极差最小

将N个数字划分成N/K组,每组K个数字,每组中的数字互不相同,求每个子数组最大最小元素的差值之和的最小值。

 

删除回文子数组

给一个字符串,每次可以移除其中一个字符,或者移除一个回文子串,求 全部移除所需最少次数

原题:Leetcode 1246 删除回文子数组 (VIP)

你可能感兴趣的:(笔试题)