Tag Archives: Hash

LeetCode Valid Triangle Number

LeetCode Valid Triangle Number Given an array consists of non-negative integers, your task is to count the number of triplets chosen from the array that can make triangles if we take them as side lengths of a triangle. Example 1:

Input: [2,2,3,4]
Output: 3
Explanation:
Valid combinations are:
2,3,4 (using the first 2)
2,3,4 (using the second 2)
2,2,3
Note:
  1. The length of the given array won’t exceed 1000.
  2. The integers in the given array are in the range of [0, 1000].

给定一个数组,问从中能取出所少个三元数组,使得取出的三个数能构成一个三角形。 首先明确三条线段构成三角形的条件是任意两边之和要大于第三遍。 先上暴力,直接dfs枚举出所有的三元组,判断能构成三角形,则方案数加1。代码如下: [cpp] class Solution { private: void dfs(int &ans, vector<int>& nums, vector<int>& cand, int idx) { if (cand.size() == 3) { int &a = cand[0], &b = cand[1], &c = cand[2]; if (a + b > c&&a + c > b&&b + c > a) { ++ans; //cout << a << "\t" << b << "\t" << c << endl; } return; } for (int i = idx; i < nums.size(); ++i) { if (cand.size() == 2 && cand[0] + cand[1] <= nums[i])return; cand.push_back(nums[i]); dfs(ans, nums, cand, i + 1); cand.pop_back(); } } public: int triangleNumber(vector<int>& nums) { int ans = 0; vector<int> cand; sort(nums.begin(), nums.end()); dfs(ans, nums, cand, 0); return ans; } }; [/cpp] 本代码提交TLE:219 / 243。数组最大长度是1000,则所有的组合数有1000*999*998=997002000,确实有点大。。。 后来我发现,报TLE的大数据,虽然有1000个数,但是有很多都是重复的,真正不同的数大概只有100个左右。所以我就想,先对数据去重,在所有互异的数中dfs。然后根据每条边重复的次数来求组合数。 比如样例中,互异的数是[2,3,4],dfs发现[2,3,4]可以构成三角形,则所有由[2,3,4]构成的三角形的个数应该是count[2]*count[3]*count[4]=2*1*1=2。 所以我们先对数组做个hash,统计数值及其出现频率的关系。注意,因为边的长度最大也只为1000,所以用一个1000长的数组来hash比用map或者unordered_map占用的内存更少,否则会MLE。 然后分三类情况进行计算:1. 三条边互不相同;2.有两条边的值相等;3.三条边的值都相等。 其中第一种情况用常规的DFS求解。第二种和第三种情况就是简单的枚举。 还需要注意一点是,边长为0的值需要过滤掉。 完整代码如下: [cpp] class Solution { private: void dfs(int& ans, vector<int>& count, vector<int>& nums, vector<int>& cand, int idx) { if (cand.size() == 3) { int &a = cand[0], &b = cand[1], &c = cand[2]; if (a + b > c&&a + c > b&&b + c > a) { ans += count[a] * count[b] * count[c]; // 三边各异 //cout << a << "\t" << b << "\t" << c << endl; } return; } for (int i = idx; i < nums.size(); ++i) { if (cand.size() == 2 && cand[0] + cand[1] <= nums[i])return; cand.push_back(nums[i]); dfs(ans, count, nums, cand, i + 1); cand.pop_back(); } } public: int triangleNumber(vector<int>& nums) { vector<int> mii(1001, 0); for (const auto& i : nums)++mii[i]; // hash vector<int> distinct; for (int i = 1; i < 1001; ++i) { if (mii[i] > 0)distinct.push_back(i); } int ans = 0; vector<int> cand; dfs(ans, mii, distinct, cand, 0); // 三边互不相同 int n = distinct.size(); for (int i = 0; i < n; ++i) { if (mii[distinct[i]] >= 3) { // 三边相同 int &d = mii[distinct[i]]; ans += (d*(d – 1)*(d – 2)) / 6; } for (int j = i + 1; j < n; ++j) { if (mii[distinct[i]] >= 2) { // 两条边一样 int &a = distinct[i], &b = distinct[i], &c = distinct[j]; if (a + b > c&&a + c > b&&b + c > a) { ans += (mii[a] * (mii[a] – 1) / 2)*mii[c]; } } if (mii[distinct[j]] >= 2) { // 两条边一样 int &a = distinct[i], &b = distinct[j], &c = distinct[j]; if (a + b > c&&a + c > b&&b + c > a) { ans += (mii[b] * (mii[b] – 1) / 2)*mii[a]; } } } } return ans; } }; [/cpp] 本代码提交AC,用时1589MS。]]>

LeetCode Bulls and Cows

299. Bulls and Cows299. Bulls and Cows

You are playing the following Bulls and Cows game with your friend: You write down a number and ask your friend to guess what the number is. Each time your friend makes a guess, you provide a hint that indicates how many digits in said guess match your secret number exactly in both digit and position (called “bulls”) and how many digits match the secret number but locate in the wrong position (called “cows”). Your friend will use successive guesses and hints to eventually derive the secret number.

Write a function to return a hint according to the secret number and friend’s guess, use A to indicate the bulls and B to indicate the cows. 

Please note that both secret number and friend’s guess may contain duplicate digits.

Example 1:

Input: secret = "1807", guess = "7810"

Output: "1A3B"

Explanation: 1 bull and 3 cows. The bull is 8, the cows are 0, 1 and 7.

Example 2:

Input: secret = "1123", guess = "0111"

Output: "1A1B"

Explanation: The 1st 1 in friend's guess is a bull, the 2nd or 3rd 1 is a cow.

Note: You may assume that the secret number and your friend’s guess only contain digits, and their lengths are always equal.299. Bulls and Cows


类似猜数字问题。假设秘密数字是”1807″,对方猜了一个数字是”7810″,问猜对了多少位,又有多少位是没猜对,但是秘密数字中出现了。比如这里猜对了第2位,都是’8’;’7’、’1’和’0’虽然没猜对位置,但是秘密数字中也有’1’、’0’和’7’,所以算完全猜对了1个数位,猜对了3个数,但位置不对,输出”1A3B”。 简单题,先对秘密数字建立数字和频率的hash表,然后遍历猜测数字,如果对应位相等,则完全猜对一个;如果对应位不相等,但是在秘密数字的hash表中,且频率大于0,则猜对第二类。 完整代码如下:

class Solution {
public:
    string getHint(string secret, string guess)
    {
        unordered_map<char, int> uci;
        for (const auto& c : secret)
            ++uci[c];
        int a = 0, b = 0;
        for (int i = 0; i < guess.size(); ++i) {
            if (guess[i] == secret[i]) {
                ++a;
                –uci[secret[i]];
            }
        }
        for (int i = 0; i < guess.size(); ++i) {
            if (guess[i] != secret[i] && uci[guess[i]] > 0) {
                ++b;
                –uci[guess[i]];
            }
        }
        return to_string(a) + "A" + to_string(b) + "B";
    }
};

本代码提交AC,用时6MS。需要注意的是,两类数字的判断需要分开,只有先判断了对应位数字完全一样的数位,才判断第二类位置不对但数对的情况。比如

Secret number:  "1122"
Friend's guess: "1222"

如果混在一起判断的话,对于guess中的第一个2,Secret是1,但Secret中有2,所以这个2被分类为位置错,但数字对的情况,从而Secret中的2的频率减少为1,导致最后一个2,虽然数字和位置完全一样,但是Secret中2的频率已经是0了,无法判断为A类。所以需要完全判断完A类之后,才判断B类。

二刷。因为只有0-9这10个数字,可以用数组代替hash:

class Solution {
public:
	string getHint(string secret, string guess) {
		int n = secret.size();
		int a = 0, b = 0;
		vector<int> s(10, 0), g(10, 0);
		for (int i = 0; i < n; ++i) {
			if (secret[i] == guess[i])++a;
			else {
				++s[secret[i] - '0'];
				++g[guess[i] - '0'];
			}
		}
		for (int i = 0; i < 10; ++i) {
			b += min(s[i], g[i]);
		}
		return to_string(a) + "A" + to_string(b) + "B";
	}
};

本代码提交AC,用时4MS。

LeetCode Longest Harmonious Subsequence

LeetCode Longest Harmonious Subsequence We define a harmonious array is an array where the difference between its maximum value and its minimum value is exactly 1. Now, given an integer array, you need to find the length of its longest harmonious subsequence among all its possible subsequences. Example 1:

Input: [1,3,2,2,5,2,3,7]
Output: 5
Explanation: The longest harmonious subsequence is [3,2,2,2,3].
Note: The length of the input array will not exceed 20,000.
一个和谐的数组是该数组的最大值和最小值只相差1。给定一个数组,求该数组的和谐子序列的最大长度。 子序列问题,又有作差问题,开始还以为是用DP来解,想了半天没想出好的解法。 后来看了一下标签用hash来做,恍然大悟。 仔细理解一下这个和谐数组的定义,最大数和最小数只相差1,说明这个子序列肯定只有两个不同的数,而且这两个数只相差1。 这就好办了,我们用hash表统计一下数组中每个数出现的频率,然后对于每个不同的数,看看该数相邻的数是否在hash表中,如果在的话,从数组中提取出这两个相邻的数的所有出现,构成的子序列就是一个和谐子序列。不断更新和谐子序列的最大长度。注意,假如考虑3构成的和谐子序列,则另一个数可能是2或者4,但是如果2在原数组中的话,已经考虑2和3构成和谐子序列的情况了,所以对于每个数,我们统一只考虑比它大1的数即可。 完整代码如下: [cpp] class Solution { public: int findLHS(vector<int>& nums) { unordered_map<int, int> hash; for (const auto &i : nums)++hash[i]; int ans = 0; for (const auto &it : hash) { if (hash.find(it.first + 1) != hash.end()) { ans = max(it.second + hash[it.first + 1], ans); } } return ans; } }; [/cpp] 本代码提交AC,用时102MS。]]>

LeetCode Minimum Index Sum of Two Lists

LeetCode Minimum Index Sum of Two Lists Suppose Andy and Doris want to choose a restaurant for dinner, and they both have a list of favorite restaurants represented by strings. You need to help them find out their common interest with the least list index sum. If there is a choice tie between answers, output all of them with no order requirement. You could assume there always exists an answer. Example 1:

Input:
["Shogun", "Tapioca Express", "Burger King", "KFC"]
["Piatti", "The Grill at Torrey Pines", "Hungry Hunter Steakhouse", "Shogun"]
Output: ["Shogun"]
Explanation: The only restaurant they both like is "Shogun".
Example 2:
Input:
["Shogun", "Tapioca Express", "Burger King", "KFC"]
["KFC", "Shogun", "Burger King"]
Output: ["Shogun"]
Explanation: The restaurant they both like and have the least index sum is "Shogun" with index sum 1 (0+1).
Note:
  1. The length of both lists will be in the range of [1, 1000].
  2. The length of strings in both lists will be in the range of [1, 30].
  3. The index is starting from 0 to the list length minus 1.
  4. No duplicates in both lists.

给定两个字符串数组,要求在两个数组中都出现的字符串在各自数组中的下标之和最小的字符串,如果有多个这样的字符串下标之和一样小,则都输出。 简单题,对两个数组,建立两个hash表,存储字符串对应下标的关系。然后遍历其中一个hash表,在如果该字符串在另一个hash表中存在,则更新下标之和。 代码如下: [cpp] class Solution { public: vector<string> findRestaurant(vector<string>& list1, vector<string>& list2) { unordered_map<string, int> hash1, hash2; for (int i = 0; i < list1.size(); ++i)hash1[list1[i]] = i; for (int i = 0; i < list2.size(); ++i)hash2[list2[i]] = i; int global_min_sum = INT_MAX; vector<string> ans; for (int i = 0; i < list1.size(); ++i) { if (hash2.find(list1[i]) != hash2.end()) { if (i + hash2[list1[i]] < global_min_sum) { global_min_sum = i + hash2[list1[i]]; ans.clear(); ans.push_back(list1[i]); } else if (i + hash2[list1[i]] == global_min_sum) { ans.push_back(list1[i]); } } } return ans; } }; [/cpp] 本代码提交AC,用时115MS。]]>

LeetCode Find Duplicate File in System

LeetCode Find Duplicate File in System Given a list of directory info including directory path, and all the files with contents in this directory, you need to find out all the groups of duplicate files in the file system in terms of their paths. A group of duplicate files consists of at least two files that have exactly the same content. A single directory info string in the input list has the following format: "root/d1/d2/.../dm f1.txt(f1_content) f2.txt(f2_content) ... fn.txt(fn_content)" It means there are n files (f1.txt, f2.txtfn.txt with content f1_content, f2_contentfn_content, respectively) in directory root/d1/d2/.../dm. Note that n >= 1 and m >= 0. If m = 0, it means the directory is just the root directory. The output is a list of group of duplicate file paths. For each group, it contains all the file paths of the files that have the same content. A file path is a string that has the following format: "directory_path/file_name.txt" Example 1:

Input:
["root/a 1.txt(abcd) 2.txt(efgh)", "root/c 3.txt(abcd)", "root/c/d 4.txt(efgh)", "root 4.txt(efgh)"]
Output:
[["root/a/2.txt","root/c/d/4.txt","root/4.txt"],["root/a/1.txt","root/c/3.txt"]]
Note:
  1. No order is required for the final output.
  2. You may assume the directory name, file name and file content only has letters and digits, and the length of file content is in the range of [1,50].
  3. The number of files given is in the range of [1,20000].
  4. You may assume no files or directories share the same name in a same directory.
  5. You may assume each given directory info represents a unique directory. Directory path and file infos are separated by a single blank space.
Follow up beyond contest:
  1. Imagine you are given a real file system, how will you search files? DFS or BFS ?
  2. If the file content is very large (GB level), how will you modify your solution?
  3. If you can only read the file by 1kb each time, how will you modify your solution?
  4. What is the time complexity of your modified solution? What is the most time consuming part and memory consuming part of it? How to optimize?
  5. How to make sure the duplicated files you find are not false positive?

给定一个文件系统,要找出所有文件内容相同的集合,分group输出。 也是简单题,对所有文件根据content Hash,Hash到同一个桶内的文件内容相同,把它们group起来就好。 需要注意是只有当一个桶内的文件数目多于1个时,才输出该桶内所有文件。 代码如下: [cpp] class Solution { private: struct MyFile { string name, directory; MyFile(string n_, string d_) :name(n_), directory(d_) {}; }; public: vector<vector<string>> findDuplicate(vector<string>& paths) { unordered_map<string, vector<MyFile>> ump; for (int i = 0; i < paths.size(); ++i) { string line = paths[i] + " "; int pos_blank = line.find(" "); string directory = line.substr(0, pos_blank); int start = pos_blank + 1, end = line.find(" ", start); while (end != string::npos) { string one_file = line.substr(start, end – start); int pos_parenthesis = one_file.find("("); string name = one_file.substr(0, pos_parenthesis); string content = one_file.substr(pos_parenthesis + 1, one_file.size() – pos_parenthesis – 2); ump[content].push_back(MyFile(name, directory)); start = end + 1; end = line.find(" ", start); } } vector<vector<string>> ans; for (const auto &it : ump) { if (it.second.size() > 1) { vector<string> group; for (const auto &f : it.second) { group.push_back(f.directory + "/" + f.name); } ans.push_back(group); } } return ans; } }; [/cpp] 本代码提交AC,用时122MS。]]>

LeetCode Kill Process

LeetCode Kill Process Given n processes, each process has a unique PID (process id) and its PPID (parent process id). Each process only has one parent process, but may have one or more children processes. This is just like a tree structure. Only one process has PPID that is 0, which means this process has no parent process. All the PIDs will be distinct positive integers. We use two list of integers to represent a list of processes, where the first list contains PID for each process and the second list contains the corresponding PPID. Now given the two lists, and a PID representing a process you want to kill, return a list of PIDs of processes that will be killed in the end. You should assume that when a process is killed, all its children processes will be killed. No order is required for the final answer. Example 1:

Input:
pid =  [1, 3, 10, 5]
ppid = [3, 0, 5, 3]
kill = 5
Output: [5,10]
Explanation:
           3
         /   \
        1     5
             /
            10
Kill 5 will also kill 10.
Note:
  1. The given kill id is guaranteed to be one of the given PIDs.
  2. n >= 1.

给定一个父子进程的对应关系,现在要kill掉某个进程id,那么id的子进程,孙进程也会被kill掉,问最终被kill掉的进程有哪些。 简单题。先使用map把父子进程的对应关系存起来,因为是一个父亲可能有多个孩子进程,所以要用multimap。然后用bfs类似于树的层次遍历,把所有孩子进程遍历一遍并存到结果数组中。 代码如下,刚好看了C++ primer的unordered_multimap的范围查找,用equal_range很方便。 [cpp] class Solution { public: vector<int> killProcess(vector<int>& pid, vector<int>& ppid, int kill) { unordered_multimap<int, int> umm; for (int i = 0; i < pid.size(); ++i) { umm.insert(pair<int, int>(ppid[i], pid[i])); } vector<int> ans; queue<int> q; q.push(kill); while (!q.empty()) { int id = q.front(); q.pop(); ans.push_back(id); auto r = umm.equal_range(id); for (auto i = r.first; i != r.second; ++i)q.push(i->second); } return ans; } }; [/cpp] 本代码提交AC,用时166MS。]]>

LeetCode Permutation in String

LeetCode Permutation in String Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1. In other words, one of the first string’s permutations is the substring of the second string. Example 1:

Input:s1 = "ab" s2 = "eidbaooo"
Output:True
Explanation: s2 contains one permutation of s1 ("ba").
Example 2:
Input:s1= "ab" s2 = "eidboaoo"
Output: False
Note:
  1. The input strings only contain lower case letters.
  2. The length of both given strings is in range [1, 10,000].

给定两个字符串s1和s2,问s1的排列是否是s2的子串。 暴力方法是枚举s2的每个字符,如果在s1中,则在s2中取等长的子串,hash判断s2的子串和s1是否是同一个串的不同排列。 代码如下: [cpp] class Solution { public: bool checkInclusion(string s1, string s2) { int n = s1.size(), m = s2.size(); if (n > m)return false; unordered_map<char, int> hash1; for (int i = 0; i < n; ++i)++hash1[s1[i]]; for (int i = 0; i < m; ++i) { if (hash1.find(s2[i]) != hash1.end()) { if (i + n <= m) { unordered_map<char, int> hash2; for (int j = i; j < i + n; ++j)++hash2[s2[j]]; if (hash1 == hash2)return true; } } } return false; } }; [/cpp] 本代码提交AC,用时1365MS。 优化方法是使用滑动窗口方法,假设s1的长度为n。先hash s1,再在s2中开长度为n的窗口,判断窗口内的子串hash是否和s1的hash相等,相等则返回tru,否则窗口向右滑动一格,即增加右边一个字符频率,同时减掉左边一个字符频率,如此循环。 代码如下: [cpp] class Solution { public: bool checkInclusion(string s1, string s2) { int n = s1.size(), m = s2.size(); if (n > m)return false; vector<int> hash1(26, 0), hash2(26, 0); for (int i = 0; i < n; ++i) { ++hash1[s1[i] – ‘a’]; ++hash2[s2[i] – ‘a’]; } if (hash1 == hash2)return true; for (int i = n; i < m; ++i) { ++hash2[s2[i] – ‘a’]; –hash2[s2[i – n] – ‘a’]; if (hash1 == hash2)return true; } return false; } }; [/cpp] 本代码提交AC,用时13MS。]]>

LeetCode Brick Wall

LeetCode Brick Wall There is a brick wall in front of you. The wall is rectangular and has several rows of bricks. The bricks have the same height but different width. You want to draw a vertical line from the top to the bottom and cross the least bricks. The brick wall is represented by a list of rows. Each row is a list of integers representing the width of each brick in this row from left to right. If your line go through the edge of a brick, then the brick is not considered as crossed. You need to find out how to draw the line to cross the least bricks and return the number of crossed bricks. You cannot draw a line just along one of the two vertical edges of the wall, in which case the line will obviously cross no bricks. Example:

Input:
[[1,2,2,1],
 [3,1,2],
 [1,3,2],
 [2,4],
 [3,1,2],
 [1,3,1,1]]
Output: 2
Explanation:

Note:
  1. The width sum of bricks in different rows are the same and won’t exceed INT_MAX.
  2. The number of bricks in each row is in range [1,10,000]. The height of wall is in range [1,10,000]. Total number of bricks of the wall won’t exceed 20,000.

hihoCoder 1494-一面砖墙完全一样的题,不解释。 代码如下: [cpp] class Solution { public: int leastBricks(vector<vector<int>>& wall) { int height = wall.size(); unordered_map<int, int> cnt; int maxedges = 0; for (int i = 0; i < height; ++i) { int len = 0; for (int j = 0; j < wall[i].size() – 1; ++j) { len += wall[i][j]; ++cnt[len]; maxedges = max(maxedges, cnt[len]); } } return height – maxedges; } }; [/cpp] 本代码提交AC,用时46MS。]]>

LeetCode Subarray Sum Equals K

560. Subarray Sum Equals K

Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k.

Example 1:

Input:nums = [1,1,1], k = 2
Output: 2

Note:

  1. The length of the array is in range [1, 20,000].
  2. The range of numbers in the array is [-1000, 1000] and the range of the integer k is [-1e7, 1e7].

给定一个数组,问有多少个子数组的和等于k。
暴力方法$O(n^3)$肯定不行,这种连续子数组的问题,肯定会用到数组的前缀和,所以先把前缀和求出来sum[i],这样任意区间[i,j]之间的和都可以用sum[i]-sum[i-1]求到。不过还是需要$O(n^2)$的时间,代码如下:

class Solution {
public:
    int subarraySum(vector<int>& nums, int k)
    {
        int n = nums.size();
        if (n == 0)
            return 0;
        vector<int> sum(n + 1, 0);
        for (int i = 1; i <= n; ++i)
            sum[i] = sum[i – 1] + nums[i – 1];
        int ans = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j <= n; ++j) {
                if (sum[j] – sum[i] == k)
                    ++ans;
            }
        }
        return ans;
    }
};

本代码提交AC,用时533MS。

看了下tag要用map来做。我们把所有前缀和及其频率存到一个map中,假设当前前缀和为sum,则如果sum-k也在map中,说明由产生sum-k前缀和的位置到当前位置的和是k。
比如样例[1,1,1],遇到第一个1时,前缀和为1,之前没有前缀和为1的情况,所以map[1]=1。当加到最后一个数时,前缀和sum=3,sum-k=1也在map中,正好说明从1开始到结束的位置的和为2,如果map[1]=2,说明有两个前缀和等于1的位置,也就有两个连续和为2的子数组。
代码如下,注意只能在遍历的同时求ans,不能等map都构建好了再求ans,这样保证前缀和为sum-k的位置肯定在i前面,也就是我们统一前缀和为k的结束位置为i,做到不重不漏。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k)
    {
        int n = nums.size();
        if (n == 0)
            return 0;
        int ans = 0, sum = 0;
        unordered_map<int, int> cnt;
        for (int i = 0; i < n; ++i) {
            ++cnt[sum];
            sum += nums[i];
            ans += cnt[sum – k];
        }
        return ans;
    }
};

本代码提交AC,用时36MS。

二刷。思路同上,但不把sum=0插入到map中,会不会更好理解一些:

class Solution {
public:
	int subarraySum(vector<int>& nums, int k) {
		int i = 0, j = 0, n = nums.size();
		map<int, int> sum_cnt;
		int sum = 0, ans = 0;
		for (int i = 0; i < n; ++i) {
			sum += nums[i];
			if (sum == k)++ans;
			if (sum_cnt.find(sum - k) != sum_cnt.end()) {
				ans += sum_cnt[sum - k];
			}
			++sum_cnt[sum];
		}
		return ans;
	}
};

本代码提交AC,用时56MS。

LeetCode Can I Win

LeetCode Can I Win In the “100 game,” two players take turns adding, to a running total, any integer from 1..10. The player who first causes the running total to reach or exceed 100 wins. What if we change the game so that players cannot re-use integers? For example, two players might take turns drawing from a common pool of numbers of 1..15 without replacement until they reach a total >= 100. Given an integer maxChoosableInteger and another integer desiredTotal, determine if the first player to move can force a win, assuming both players play optimally. You can always assume that maxChoosableInteger will not be larger than 20 and desiredTotal will not be larger than 300. Example

Input:
maxChoosableInteger = 10
desiredTotal = 11
Output:
false
Explanation:
No matter which integer the first player choose, the first player will lose.
The first player can choose an integer from 1 up to 10.
If the first player choose 1, the second player can only choose integers from 2 up to 10.
The second player will win by choosing 10 and get a total = 11, which is >= desiredTotal.
Same with other integers chosen by the first player, the second player will always win.

又是一道博弈论的题。两个人从1~n中拿数字,问哪个人拿到的数字之和先大于等于desiredTotal。 边界条件是,如果n大于等于desiredTotal,则A开始拿最大数肯定能赢;另一种情况是,如果n个数之和都小于desiredTotal,则无论怎么拿都不可能超过desiredTotal,A不能获胜。 我们用一个长度为n的字符串表示n个数字被拿的状态,’0’表示没被拿,’1’表示被拿了,当然可以用int和位运算来表示,我这里用string是偷懒了。 再用一个hash[string][bool]表示当n个数字的状态为string时A获胜的情况。那么每次A可以从string中为’0’的位置拿数字,如果此时sum + i >= desiredTotal则A肯定获胜;或者sum + i < desiredTotal但是下一轮B没有获胜,即 !win(sum + i, desiredTotal, visited, result),A也是获胜的。 代码如下: [cpp] class Solution { private: bool win(int sum, int &desiredTotal, string &visited, unordered_map<string,bool>& result) { if (result.find(visited) != result.end())return result[visited]; for (int i = 1; i < visited.size(); ++i) { if (visited[i] == ‘0’) { visited[i] = ‘1’; if (sum + i >= desiredTotal || !win(sum + i, desiredTotal, visited, result)) { visited[i] = ‘0’; //reset result[visited] = true; return true; } visited[i] = ‘0’; //reset } } result[visited] = false; return false; } public: bool canIWin(int maxChoosableInteger, int desiredTotal) { if (maxChoosableInteger >= desiredTotal)return true; int sum = (1 + maxChoosableInteger)*maxChoosableInteger / 2; if (sum < desiredTotal)return false; string visited(maxChoosableInteger + 1, ‘0’); unordered_map<string, bool> result; return win(0, desiredTotal, visited, result); } }; [/cpp] 本代码提交AC,用时309MS。]]>