阿里的笔试,怎么说呢,感觉挺难的,选择会个一半,编程题也只写了一题,是我看不懂题目,第一题调试了半小时,一个小时写两道,这一刻感觉自己编程太菜了,得好好练,刷刷题才行。直接上题目吧。

1、小明是一个数学家,他喜欢用数字给事物命名编号,他给自己编号为1,同时在2019年小明开办了一个农场,准备开始养母猪,他专门给农场的母猪用以下数列2,3,4,5,7,9,12,16,21,28,37,49,65,86,114,151...进行命名。假设农场的母猪永远不会死,小母猪出生后3年后成熟,成熟后从第三年开始每年只会生一只小母猪。第一年农场,有一只刚刚出生的小母猪和一只成熟的母猪(本年不再生小猪,下一年开始生小猪),并给他们编号为2和3。请问,第m只母猪编号为多少?其是哪一年出生的?小明还准备了1份礼物,专门颁给农场第1到m只的母猪颁奖,颁奖规则如下:选出第1到m只的母猪翻转编号(114编号翻转为411)为第k大的母猪进行颁奖,请问是第几只猪获奖?提示: f(n)=f(n-2)+f(n-3)

我先说下这题的解决思路:

(1)求num,f(n)=f(n-2)+f(n-3)

(2)母猪什么时候要生小猪,而且每只成***猪一年只能生一只小猪

(3)取1-m只小猪,对小猪按num反转进行排序

为了解决上述问题,可以定义一个类,根据题意,可以提取的属性有:出生序号1-m,出生年份born,规则编号num,以及年龄old。于是构建如下类:

    static class Pig{
        public int index;//出生序号
        public int born;//出生年份
        public int num;//规则序号
        public int old;//年龄
        public Pig(int index,int born,int num,int old){
            this.index = index;
            this.born = born;
            this.num = num;
            this.old = old;
        }

        @Override
        public String toString() {
            return "Pig{" +
                    "index=" + index +
                    ", born=" + born +
                    ", num=" + num +
                    ", old=" + old +
                    '}';
        }
    }

接着求解求f(n)=f(n-2)+f(n-3),用动态规划,状态转移方程很简单就是res[i]=res[i-2]+res[i-3]:

    public static int getNum(int n){//动态规划求num
        if(n + 1 < 5)return n + 1;
        int res[] = new int[n + 1];
        res[0] = 1;
        res[1] = 2;
        res[2] = 3;
        res[3] = 4;
        for(int i=4;i<n+1;i++){
            res[i] = res[i-2] + res[i-3];//状态转移方程
        }
        return res[n];
    }

由于编号小明是1,故第n+1个编号才是第n只猪的编号。

对1-m只小猪按num反转排序,即对小于等于num的所有小猪,先反转num,再按num排序,然后求第k大:

    public static int getRewardNum(List<Pig> pigList, int numM, int k){
        List<Pig> list = new ArrayList<>();
        //求第一到第m只猪,并把num反转
        for(Pig pig: pigList) if(pig.num <= numM) list.add(new Pig(pig.index,pig.born,Integer.parseInt(new StringBuilder(pig.num+"").reverse().toString()),pig.old));
        list.sort(((o1, o2) -> Integer.compare(o1.num,o2.num)));//根据num排序
        if(k<0||k>list.size())return 0;//k不合法直接返回0
        return list.get(list.size() - k).index;//返回第k大的出生序号
    }

为什么前面我说看不懂题目,调试半天,就是在这:

假设农场的母猪永远不会死,小母猪出生后3年后成熟,成熟后从第三年开始每年只会生一只小母猪。

到底是小猪出生时old赋值0还是1,到底是old大于等于3(成熟了马上能生小猪)就能生小猪,还是大于3(成熟一年后)才能生小猪,也许我语文太差,我真的不明白,于是怎么调试born都出不来,后面只能强行“缩短”长成熟的时间,于是old初始赋值1,old大于等于3就能生小猪。我擦,竟然通过了,这期间花了我20分钟调试。

    public static List<Pig> getPigList(int m){//得到第m只猪出生时的所有猪
        List<Pig> pigList = new ArrayList<>();
        int curYear = 2019;
        pigList.add(new Pig(1,curYear,getNum(1),3));//第一只成***猪
        pigList.add(new Pig(2,curYear,getNum(2),1));//第一只小母猪
        int startIndex = 3;
        int targetNum = getNum(m);
        while ( pigList.get(pigList.size()-1).num <= targetNum){
            List<Pig> youngPigList = new ArrayList<>();
            curYear++;
            int lastNumPig = pigList.size();
            for(int i=0;i<pigList.size();i++){
                pigList.get(i).old++;//年龄加1
                if(pigList.get(i).old>=3)//可以生小猪了
                    youngPigList.add(new Pig(startIndex++,curYear,getNum(lastNumPig++ + 1),1));
            }
            for (Pig p:youngPigList) pigList.add(p);
        }
        for (Pig p:pigList) System.out.println(p);
        return pigList;
    }

main方法:

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String strs[] = sc.nextLine().split(",");
        int m = Integer.parseInt(strs[0]), k = Integer.parseInt(strs[1]);
        List<Pig> pigList = getPigList(m);
        Pig targetPig = pigList.get(m-1);
        System.out.printf("%d,%d,%d\n",targetPig.num,targetPig.born,getRewardNum(pigList,targetPig.num,k));
        sc.close();
    }

调试结果:

2、小明在双十一晚会上抽奖赢得了一次天猫超市免单的机会,享受在一个包裹内最大体积V,最大重量M内免单
假设商品i,体积Vi,重量Mi,库存Si,价格Pi目前天猫超市的商品分为生鲜水产(1)、食品酒水(2),美妆个护(3),居家生活(4)四大类
生鲜水产不与美妆个护同包裹请你帮助小明在购物车里添置商品使得总价值最大 。

按一个小时来说,第一题调试花了很多时间,第二题根本没时间写,后面看,根本不会写,就有回溯的想法,用HashMap记录出现过的序列,后面发现,每个满足的序列长度不定长,所以懵了。总结,被安排得明明白白,以后要多多练习才行。

 

这用例的答案奇怪,如果说总体积v,总重量w以内是包含的话,应该是这么算:

第3种物品取5个得到v=15,w = 20, s = 1, p = 25, 然后在第2种物品中再取一个此时v=28,w = 30,p = 36。

如果是不包含的话应该是这么算:

第2种物品取2个得到v=26,w = 20, s = 10, p = 22, 然后在第3种物品中再取2个此时v=32,w = 28,p = 32。

两种情况都不可能算得33啊,不知道是不是我理解错了,我的参考代码:

#include<iostream>
#include<vector>
using namespace std;
struct good
{
    int v, w, p, t;
};
int alibishi3(int N, int V, int W, vector<good>& goods){
    vector<vector<int>> f(60, vector<int>(60, 0));
    for(auto g : goods){
        for (int j = V; j >= g.v; j--)
        {
            for (int k = W; k >= g.w; k--)
            {
                f[j][k] = max( f[j][k], f[j - g.v][k - g.w] + g.p);
            }
        }
        
    }
    return f[V][W];    
}

int main()
{
    int N,V,W;  
    cin >> N >> V >> W;
    
    vector<good> goods;
    for(int i = 0; i < N; i++){
        int v, w, s, p, t;
        cin >> v >> w >> s >> p >> t;
        for (int k = 1; k <= s; k *= 2){
            s -= k;
            goods.push_back({k * v, k * w, k * p, t});
        }
        if(s > 0) goods.push_back({s * v, s * w, s * p, t});
    }
    vector<good> goods1;//不包含种类3的
    vector<good> goods3;//不包含种类1的
    for(auto g : goods)
        if(g.t == 1)goods1.push_back(g);
        else if(g.t == 3)goods3.push_back(g);
        else goods1.push_back(g), goods3.push_back(g);
    int res = max(alibishi3(N,V,W,goods1), alibishi3(N,V,W,goods3));
    cout << res << endl;
    system("pause");
    return 0;
}