笔试题练习(滴滴17年真题)

1.连续最大和

题目

一个数组有 N 个元素,求连续子数组的最大和。 例如:[-1,2,1],和最大的连续子数组为[2,1],其和为 3 

输入描述:
输入为两行。 第一行一个整数n(1 <= n <= 100000),表示一共有n个元素 第二行为n个数,即每个元素,每个整数都在32位int范围内。以空格分隔。
输出描述:
所有连续子数组中和最大的值。

题目分析

法一:可以找出规律,如果当前和小于0,那么可以把这个当前值抛弃,因为加上一个负数(包括0)还不如不加,把当前数设置为当前和。如果当前和是正数,则把当前数加进去更新当前和,这个时候判断下当前和是否大于最大连续和,是的话就更新最大连续和,否则,不更新最大连续和,只更新当前和。需要注意的是:需要设置一个布尔变量,判断是因为输入无效数据返回的0还是本身最大连续和就是0.

法二:用动态规划的算法。用f(i)表示第一个数字结尾的子数组的最大和,那么我们需要求出max{f(i)},其实0<=i<=n。然后用递归来解。代码同法一是一样的,max[f(i)]就是greatsum,f(i)就是cursum。

 f(i)  =data[i]                  i==0 ||  f(i-1)<=0

         =f(i-1)+data[i]     i!=0  && f(i-1)>0

代码

#include<iostream>
 
using namespace std;
 
#include<vector>
 
bool g_invalid= false ; //判断返回值为0 还是因为是无效数据返回的0
 
int getMax(vector< int > nums, int n)
{
     if (n<=0)
     {
         g_invalid= true ;
         return 0;
     }
 
    g_invalid= false ;
 
     int cursum=0;
     int greatsum=0x80000000;
     for ( int i=0;i<n;i++)
     {
         if (cursum<=0)
             cursum=nums[i];
         else
             cursum+=nums[i];
         if (cursum>greatsum)
             greatsum=cursum;
     }
     return greatsum;
}
int main()
{
     int total_numbers=0;
     cin>>total_numbers;
     vector< int >  s;
     int tmp=0;
     for ( int i=0;i<total_numbers;i++)
     {
         cin>>tmp;
         s.push_back(tmp);
     }
     cout<<getMax(s,total_numbers);
     return 0;
}

2.餐馆

题目

某餐馆有n张桌子,每张桌子有一个参数:a 可容纳的最大人数; 有m批客人,每批客人有两个参数:b人数,c预计消费金额。 在不允许拼桌的情况下,请实现一个算法选择其中一部分客人,使得总预计消费金额最大 

输入描述:
输入包括m+2行。 第一行两个整数n(1 <= n <= 50000),m(1 <= m <= 50000) 第二行为n个参数a,即每个桌子可容纳的最大人数,以空格分隔,范围均在32位int范围内。 接下来m行,每行两个参数b,c。分别表示第i批客人的人数和预计消费金额,以空格分隔,范围均在32位int范围内。
输出描述:
输出一个整数,表示最大的总预计消费金额

题目分析

    首先来看数据结构,对于餐桌而言,需要知道它的容纳数量以及是否以及被占座了,对于顾客而言,我们需要知道它的就餐人数和消费金额,因此,采用类或者结构体来存储这两个变量最合适,然后将读入的类或者结构体存入到vector里面,因为容器里面存储的元素可以是自定义的类或者数据结构,对于排序也比较方便。接下来分析如何收益最大化,我们当然希望土豪先入座,而且坐一个符合他们就餐人数的容纳量最小的桌子,这就需要我们将顾客这个类进行排序,先按消费金额进行降序排列,在消费金额相同的情况下,按人数的多少升序排列。为了符合坐一个符合他们就餐人数的容纳量最小的桌子,对应的餐桌这个类也应该按照人数进行升序排列。这样,在满足能够容纳就餐人数且没有被占座的情况下,依次安排顾客就座,就可以达到收益最大化。需要注意的是,收益总金额的数据类型,应该定义为long long类型,否则会引起溢出。

代码

#include<iostream> 
#include<algorithm> 
#include <vector>  
using namespace std; 
   
class Customer               //声明顾客类 
    
         public
         int num;             //每批顾客人数 
         int money;           //每批顾客预计消费金额 
         Customer() 
        
             num = 0; 
             money = 0; 
        
     }; 
   
class Table                  //餐桌类 
    
         public
         int num;             //每张餐桌能容纳人数 
         int state;           //每张餐桌状态,0为空,1为满 
         Table() 
        
             num = 0; 
             state = 0; 
        
     }; 
   
   
//先按照消费金额进行降序,金额一样时按照人数升序   
bool sortByAll( const Customer &c1,  const Customer &c2) 
     if (c1.money > c2.money) 
         return true
     else if (c1.money < c2.money) 
         return false
     else 
         return c1.num < c2.num; 
   
//餐桌按能容纳人数升序 
bool sortBynum( const Table &t1,  const Table &t2) 
     return t1.num < t2.num; 
   
int main() 
     int n, m; 
     int i, j; 
     long long Max_money = 0; 
       
     cin >> n >> m; 
     vector<Table> vec_T(n); 
     vector<Customer> vec_C(m); 
     for (i = 0;i < n;i++)                             //输入各餐桌能容纳人数 
         cin >> vec_T[i].num; 
     for (j = 0;j < m;j++)                             //输入顾客人数及预计消费金额 
         cin >> vec_C[j].num >> vec_C[j].money; 
     sort(vec_C.begin(), vec_C.end(), sortByAll);     //顾客按金额降序,按人数升序 
     sort(vec_T.begin(), vec_T.end(), sortBynum);     //餐桌按能容纳人数升序 
   
     for (i = 0;i < m;i++)             //按消费金额依次安排入座 
         for (j = 0;j < n;j++) 
             if (vec_C[i].num <= vec_T[j].num && vec_T[j].state == 0) 
            
                 vec_T[j].state = 1; 
                 Max_money += vec_C[i].money; 
                 break
            
     cout << Max_money << endl; 
   
     return 0; 
}

3.地下迷宫

题目

小青蛙有一天不小心落入了一个地下迷宫,小青蛙希望用自己仅剩的体力值P跳出这个地下迷宫。为了让问题简单,假设这是一个n*m的格子迷宫,迷宫每个位置为0或者1,0代表这个位置有障碍物,小青蛙达到不了这个位置;1代表小青蛙可以达到的位置。小青蛙初始在(0,0)位置,地下迷宫的出口在(0,m-1)(保证这两个位置都是1,并且保证一定有起点到终点可达的路径),小青蛙在迷宫中水平移动一个单位距离需要消耗1点体力值,向上爬一个单位距离需要消耗3个单位的体力值,向下移动不消耗体力值,当小青蛙的体力值等于0的时候还没有到达出口,小青蛙将无法逃离迷宫。现在需要你帮助小青蛙计算出能否用仅剩的体力值跳出迷宫(即达到(0,m-1)位置)。 
输入描述:
输入包括n+1行:
 第一行为三个整数n,m(3 <= m,n <= 10),P(1 <= P <= 100)
 接下来的n行:
 每行m个0或者1,以空格分隔
输出描述:
如果能逃离迷宫,则输出一行体力消耗最小的路径,输出格式见样例所示;如果不能逃离迷宫,则输出"Can not escape!"。 测试数据保证答案唯一

题目分析

    还没有熟悉dfs,后面再更新,这里的代码参考牛客网上的。

代码

#include<iostream>
#include<vector>
using namespace std;
#define VISITED 2;
int n,m,p;
int maze[10][10];//存迷宫
int dir[4][2]={{0,-1},{0,1},{-1,0},{1,0}};//左右上下
int cost[4]={-1,-1,-3,0};
int final_p=-200;
//存储各点的数据结构
struct mazepoint
{
    mazepoint(int _x,int _y):x(_x),y(_y){}
    int x,y;
};
//存储最终的最优路径
vector<mazepoint> minCostPath;
//存储每次遍历的路径
vector<mazepoint> pathStack;
void printPath(const vector<mazepoint>& path)
{
    for(int i=0;i<path.size();i++)
    {
        cout<<"["<<path[i].x<<","<<path[i].y<<"]";
        if(i<path.size()-1)
        {
            cout<<",";
        }
    }
}
void search(int x,int y,int cur_P)
{
    pathStack.push_back(mazepoint(x,y));
    maze[x][y]=VISITED;
    //到达终点了且还有体力
    if(x==0 && y==m-1 && cur_P>=0)
    {
        if(cur_P>final_p)//更新final_p,如果下一次找出来的路径比当前的这个路径所剩的体力更多,则继续更新
        {
            final_p=cur_P;
            minCostPath=pathStack;
        }
        pathStack.pop_back();
        maze[x][y]=1;// 为了回退至之前的节点,将当前结点弹出
        return ;
    }
    //没到终点还有体力,向上下左右四个方向探索,计算相应的消耗,如果新的点在边界内且为可达点,递归调用srearch函数
    if(cur_P>0)
    {
        for(int i=0;i<4;++i)
        {
            int nx=x+dir[i][0];
            int ny=y+dir[i][1];
            int nP=cur_P+cost[i];
            if(nx>=0 && nx<n && ny>=0 && ny<m && maze[nx][ny]==1)
                search(nx,ny,nP);
        }
    }
    pathStack.pop_back();    // 为了回退至之前的节点,将当前结点弹出
    maze[x][y] = 1;    // 注意:之前maze[x][y]被标记为VISITED(值为2),回退后应该将其还原为1
}
int main()
{
    cin>>n>>m>>p;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            cin>>maze[i][j];
        }
    }
    search(0,0,p);//搜索最短路径
    if(final_p!=-200)//能走出去
    {
        printPath(minCostPath);
    }
    else
        cout<<"Can not escape!";
    return 0;
}

4.末尾0的个数

题目

输入一个正整数n,求n!(即阶乘)末尾有多少个0? 比如: n = 10; n! = 3628800,所以答案为2 
输入描述:
输入为一行,n(1 ≤ n ≤ 1000)
输出描述:
输出一个整数,即题目所求

题目分析

    产生0,则必然由2和5相乘而来,所以这道题成了求因子数,显然2的因子是要多于5的因子的,因此,结果末尾由多少个0取决于它由多少个5的因子,我们可以遍历1到n,然后看它由多少个5的因子。但是这样做不是最佳的,只有5的倍数才有5这个因子,能被5整除提供1个0,能被25整除提供2个0,能被125整除提供3个0,所以

结果=n/5+n/25+n/125...

代码

#include<iostream>
#include<vector>
using namespace std;
int main()
{
     int n;
     cin>>n;
     int res;
     while (n)
     {
         res+=n/5;
         n=n/5;
     }
     cout<<res<<endl;
}

5.进制转换

题目

给定一个十进制数M,以及需要转换的进制数N。将十进制数M转化为N进制数 
输入描述:
输入为一行,M(32位整数)、N(2 ≤ N ≤ 16),以空格隔开。
输出描述:
为每个测试实例输出转换后的数,每个输出占一行。如果N大于9,则对应的数字规则参考16进制(比如,10用A表示,等等)

题目分析

短除取余,逆序,所以考虑用栈的结构去存储m%n,然后把栈一个个弹出来,这里要注意负数的情况,可以先把负数转成正数再处理

代码

#include <iostream>
#include <stack>
using namespace std;
int main(int argc, const char * argv[]) 
{
    int M,N;
    cin>>M>>N;
    stack<char> answer;
    if(M<0)
    {
        cout<<"-";
        M=-M; 
    }
    while(M!=0)
    {
 
        if(M%N<10)
        {
            answer.push(char(M%N+'0'));
            M/=N;
        }
        else 
        {
            answer.push(char(M%N-10+'A'));
            M/=N;
        }
    }
    while(!answer.empty())
    {
        cout<<answer.top(); 
        answer.pop();
    }
    return 0;
}

6.数字和为sum的方法数

题目

给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。 
输入描述:
输入为两行:
 第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
 第二行为n个正整数A[i](32位整数),以空格隔开。
输出描述:
输出所求的方案数

题目分析

代码

//动态规划 dp[i][j]表示用前i个值组成和为j的方案个数
//结果int类型存不下,得64位的数据类型,dp也不能在main函数里面定义,栈存不下,因此存在全局数据区
#include<iostream>
#include<algorithm>
using namespace std;
long long dp[1000][1000];
int main()
{
    int n,sum;
    cin>>n>>sum;
    int p[1000];
    for(int i=1;i<=n;i++)
         cin>>p[i];
    //初始化dp,前i个数组组成和为0的方案只有1种,就是什么都不选
    for(int i=0;i<n;i++)
    {
        dp[i][0]=1;
    }
    //初始化dp,用0个元素没法组成1至sum
    for(int j=1;j<sum;j++)
    {
        dp[0][j]=0;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=sum;j++)
        {
            if(p[i]<=j) 
                dp[i][j]=dp[i-1][j]+dp[i-1][j-p[i]];
            else
                dp[i][j]=dp[i-1][j];
        }
    }
    cout<<dp[n][sum]<<endl;
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值