LintCode 解题记录 2017.6.3

主要记录一下昨天做的5道题目。有3道题目没有在给定时间内AC,也是菜的咯。

第一题 LintCode Coins in a line
给定一排n个硬币,从第一个玩家开始从右边取硬币,每次可取一个或者两个硬币,两位玩家轮流进行,取走最后一枚硬币的玩家获胜。问给定n,判断玩家1是否能否获胜?
思路:反向递推法。要想玩家1能取走最后一枚硬币,那么玩家1一定要取走第4枚硬币;要想玩家1能取走第4枚硬币,那么玩家1一定要取走第7枚硬币;以此类推,玩家1获胜的条件就是一定要能取走第3k+1枚硬币。那么给定n,玩家1第一次取几枚硬币可以保证取到第3k+1枚硬币呢?由于第一次玩家1既能取1枚也能取2枚,所以当 n=3k+1或者n=3k+2的时候可以保证玩家1取到第3k+1枚硬币。因此代码很简单,这道题目重点考察了数学思想的应用:

class Solution {
public:
    /**
     * @param n: an integer
     * @return: a boolean which equals to true if the first player will win
     */
     bool firstWillWin(int n) {
        // write your code here
        return n % 3 != 0;
    }
};

第二题 Coins in a line II
这道题目是上一题的变形,即最后谁取得的总价值大谁就获胜。但是不能通过简单的数学思路来做了,需要用动态规划的思想来做。所谓动态规划,就是“动着”规划。最关键的步骤就是找到状态表示,确定递推关系。我们用dp[i]来表示玩家从第i枚硬币开始游戏到最后一枚硬币可获得的最大利润。我们从最后一步开始考虑:
1.只有一个硬币,那肯定拿了比不拿好,dp[n-1]=values[n-1];
2.只有两个硬币,那也肯定是全拿了好,否则还得给对手留一个,所以dp[n-2]=values[n-2]+values[n-1];
3.剩下三个硬币,无论我只拿一个还是拿两个反正是拿不到最后一个硬币,所以我肯定是拿两个只给对手留下最后一个硬币,所以dp[n-3]=values[n-3]+values[n-2];
那么接下来考虑一般的情况,假设目前我(玩家1)从硬币i开始选择。
1.我只拿一个硬币,那么对手(玩家2)就从i+1硬币开始,由于对手也有两种选择,即1)对手只拿一个硬币,那么再次轮到我时就是从第i+2枚硬币开始,此时我能获得的最大利润就是dp[i+2],总利润就是values[i]+dp[i+2]。2)对手拿两个硬币,那么同理就是valuse[i]+dp[i+3]。那么对手会如何选择呢?毫无疑问,他肯定会选择两种选择中让我利润最小的那一种,也就是说是values[i]+min(dp[i+2], dp[i+3])
2.我拿了两枚硬币,那么跟上面同样的分析方法,可以得到此时我能获得的利润就是values[i]+values[i+1]+min(dp[i+3], dp[i+4])。
那么我的选择肯定就是两者中的最大值了,这样就得到了递推关系式:
dp[i] = max(values[i]+min(dp[i+2], dp[i+3]), values[i]+values[i+1]+min(dp[i+3], dp[i+4]))。
最后dp[0]就是我从第0枚硬币开始选所能获得的最大利润。那么另一位玩家获得的利润就是总利润-dp[0]。
只需要判断这两个利润哪一个更大从而得出谁会获胜即可。
那么代码如下:

class Solution {
public:
    /**
     * @param values: a vector of integers
     * @return: a boolean which equals to true if the first player will win
     */
    bool firstWillWin(vector<int> &values) {
        // write your code here
        if (values.size() < 3) return true;
        int n = values.size(), sum = 0;
        vector<int> dp(n+1, 0);//dp[i+4]会出现dp[n]的情况,不如就假设dp[n]=0;
        dp[n-1] = values[n-1];
        dp[n-2] = values[n-2] + values[n-1];
        dp[n-3] = values[n-3] + values[n-2];
        for (int i = n-4; i>= 0; i--) {
            dp[i] = max(values[i]+min(dp[i+2], dp[i+3]), 
                values[i]+values[i+1]+min(dp[i+3], dp[i+4]));
        }
        for (auto value: values) {
            sum += value;
        }
        return dp[0] > sum - dp[0];
    }
};

第三题 Combination Sum
dfs题目。

class Solution {
public:
    /**
     * @param candidates: A list of integers
     * @param target:An integer
     * @return: A list of lists of integers
     */
    vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
        // write your code here
        vector<vector<int>> ret;
        set<vector<int>> s; //用set避免重复
        vector<int> tmp;
        int sum = 0;
        if (candidates.size() == 0) return ret;
        sort(candidates.begin(), candidates.end());
        for (int i = 0; i < candidates.size(); i++) {
            dfs(candidates, s, tmp, i, sum, target);
        }
        for (auto list : s) {
            ret.push_back(list);
        }
        return ret;
    }

    void dfs(vector<int> candidates, set<vector<int>> &s, vector<int> tmp,
        int idx, int sum, int target) {
        //这里sum和tmp都是用了回溯法,因为普通值传递修改形参只会在该函数内有效,退出这个函数
        //的时候自动还原成了之前的实参。
        sum += candidates[idx];
        tmp.push_back(candidates[idx]);
        if (sum >= target) {
            if (sum == target) 
                s.insert(tmp);
            return;
        }
        for (int i = idx; i < candidates.size(); i++) {
        //从idx开始的原因是 每一个数字可以无限次使用
            dfs(candidates, s, tmp, i, sum, target);
        }
    }
};

第四题 Combination SumII
几乎和上一题没有什么变化。

class Solution {
public:
    /**
     * @param num: Given the candidate numbers
     * @param target: Given the target number
     * @return: All the combinations that sum to target
     */
    vector<vector<int> > combinationSum2(vector<int> &num, int target) {
        // write your code here
        set<vector<int>> s;
        vector<vector<int>> ret;
        vector<int> tmp;
        int sum = 0;
        if (num.size() == 0) return ret;
        sort(num.begin(), num.end());
        for (int i = 0; i < num.size(); i++) {
            dfs(num, s, tmp, i, sum, target);
        }
        for (auto list : s) {
            ret.push_back(list);
        }
        return ret;
    }

    void dfs(vector<int> &num, set<vector<int>> &s, vector<int> tmp, int idx,
        int sum, int target) {
        sum += num[idx];
        tmp.push_back(num[idx]);
        if (sum >= target) {
            if (sum == target)
                s.insert(tmp);
            return;
        }
        for (int i = idx+1; i < num.size(); i++) {
            dfs(num, s, tmp, i, sum, target);
        }
    }
};

第五题 Container With Most Water
思路:Two Points法。用一左一右指针从两端向中间聚集,当左边高度小于右边时,left++;否则right–;
为什么呢?因为当左边高度小于右边时,left++意味着我们舍弃了从left到right-1,right-2等等这些水桶。因为这些水桶的面积一定小于left到right。可以这样想由于水桶的底缩短了,所以要想面积比left到right的大,那么高一定要变大。但实际上height = min(height[left], height[right]) <=height[left],所以这个新高一定不会比之前的那个高大。所以面积一定是变小的。

class Solution {
public:
    /**
     * @param heights: a vector of integers
     * @return: an integer
     */
    int maxArea(vector<int> &heights) {
        // write your code here
        int l = 0, r = heights.size()-1;
        int ret = 0, area = 0;
        while (l < r) {
            area = (r-l) * min(heights[l], heights[r]);
            ret = max(ret, area);
            if (heights[l] < heights[r]) l++;
            else r--;
        }
        return ret;
    }
};

总结:
1.第1、3、5是没有AC的,动态规划确实不熟,而且个人的算法思路仍然很狭窄。
2.感觉一天5道Medium的题目的量刚刚好,做题+看答案+写blog花差不多3个多小时。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值