思路:拿到这个问题,感觉像是动态规划。得想办法把子问题拆出来。但是一般情况下,并不是这么好拆的。所以从最简单的用例开始。假设只有两个数,这很简单,无论怎么拿,得分都一样。再加一个数呢?你就要考虑从哪里先拿,然后再从哪里先拿的问题。假设现在有三个数,你从前端拿了一个数,则下一步你得考虑从哪里拿的增益比较大。从前端拿得到一个front_gain, 从末端拿得到一个back_gain. 你得比一比哪个会让你的得分最大化。然后下一步继续这样考虑,这就形成了一个递归步。就是说你已经拆出来了!专业店说就是你找除了递推关系式。 题外话:Case analysis is more powerful than you thought!
/** * @breif Calculate the max score of the given array. * @param arr The origin array. * @param beg The begining of the range. * @param end The end of the range. * @param isFront Is the last taken from the front or not. * @return The max score in a paticular setting. * * Note that the @c arr is the origin array, and [beg, end) * range is considered from the second step, with @c isFront. * For example: * Suppose @c arr.size() = 5 * If @c isFront = true, means the last taken is from the front, * then [beg, end) = [1, 5). * If @c isFront = false, means the last taken is from the back, * then [beg, end) = [0, 4). */ intopt(constvector<int>& arr, int beg, int end, bool isFront){ // base cases if (beg >= end) return0; if (end - beg == 1) { if (isFront) returnabs(arr[beg] - arr[beg-1]); else returnabs(arr[beg] - arr[end]); }
// ELSE int front_gain = 0; // the gain if take front at current step int back_gain = 0; // the gain if take back at current step if (isFront) { front_gain = abs(arr[beg] - arr[beg-1]); back_gain = abs(arr[end-1] - arr[beg-1]); } else { front_gain = abs(arr[beg] - arr[end]); back_gain = abs(arr[end-1] - arr[end]); } return max( front_gain + opt(arr, beg+1, end, true), back_gain + opt(arr, beg, end-1, false)); }
intmain() { int N = 0; cin >> N; vector<int> arr; for (int i=0; i!=N; ++i) { int tmp; cin >> tmp; arr.push_back(tmp); }
今天,在冬木市举行了一场盛大的舞会。参加舞会的有n 位男士,从 1 到 n 编号;有 m 位女士,从 1 到 m 编号。对于每一位男士,他们心中都有各自心仪的一些女士,在这次舞会中,他们希望能与每一位自己心仪的女士跳一次舞。同样的,对于每一位女士,她们心中也有各自心仪的一些男士,她们也希望能与每一位自己心仪的男士跳一次舞。在舞会中,对于每一首舞曲,你可以选择一些男士和女士出来跳舞。但是显然的,一首舞曲中一位男士只能和一位女士跳舞,一位女士也只能和一位男士跳舞。由于舞会的时间有限,现在你想知道你最少需要准备多少首舞曲,才能使所有人的心愿都得到满足?
intmain() { // read input and built heartbeat matrix int num_men, num_women, num_likes, val; std::cin >> num_men >> num_women; int total_num = num_men + num_women; // the heartbeat matrix vector<vector<int> > mat(total_num, vector<int>(total_num, 0));
for (int i = 0; i != total_num; ++i) { std::cin >> num_likes; while (num_likes--) { std::cin >> val; if (i < num_men) // man to woman mat[i][val+num_men-1] = 1; else// woman to man mat[i][val-1] = 1; } } // done // count for each persons hearbeats int songs_needed = 0; for (int i = 0; i != total_num; ++i) { int cnt = 0; if (i < num_men) // counting for each man { for (int j = num_men; j != total_num; ++j) { if (mat[i][j] == 1) // man i like woman j ++cnt; elseif (mat[j][i] == 1) // man i is liked by woman j ++cnt; } } else { for (int j = 0; j != num_men; ++j) { if (mat[i][j] == 1) ++cnt; elseif (mat[j][i] == 1) ++cnt; } } if (songs_needed < cnt) songs_needed = cnt; // update songs if needed } std::cout << songs_needed; return0; }
classSolution { public: Solution(int n, int m) { // fill with 0 for (int i = 0; i != n; ++i) mat.push_back(vector<int>(m, 0)); }
voidskewFill(int n, int m) { int cnt = 1; // fill the up-left triangle for (auto col = 0; col != m; ++col) { for (auto i = 0, j = col; i != n && j >= 0; ++i) mat[i][j--] = cnt++; } // fill the bottom-right triangle for (auto row = 1; row != n; ++row) { for (auto j = m-1, i = row; i != n && j >= 0; ++i) mat[i][j--] = cnt++; } }
/// print the matrix voidprinter() { for (auto &row : mat) { for (auto &col : row) printf("%3d ", col); cout << endl; } }
private: vector<vector<int>> mat; };
// test intmain() { int n = 0, m = 0; cin >> n >> m; Solution s(n, m); s.skewFill(n, m); s.printer(); return0; }
题七:螺旋矩阵
按顺时针填充螺旋填充矩阵。例如:
1 2 3 4 5 6 7
9 8 7 2 1 6 3 4 5 (3x3)
12 11 10 9 3 2 1 8 4 5 6 7 (3x4)
Idea: Imagine there are for borders that surround the matrix. We walk through the mat, and once achieve a border, we change the direction. Each time we finished a circle, we will shrink our border, and start next circle. After we fill (row * col) elems in the matrix, we are done!
Note: this solution is the most elegant in my opinion, for it’s simple boundray case.
voidBuildMat() { // 4 border int top = 0; int bot = mat_.size() - 1; int left = 0; int right = mat_[0].size() - 1;
int N = (bot + 1) * (right + 1); for (int i = 0, j = 0; N != 0; --N) { // make sure fill exactly one element at each loop mat_[i][j] = N;
if (i == top) { if (j < right) ++j; // go right elseif (j == right) ++i; // checkpoint } elseif (j == right) { if (i < bot) ++i; // go down elseif (i == bot) --j; } elseif (i == bot) { if (j > left) --j; // go left elseif (j == left) --i; } elseif (j == left) { if (i > top + 1) --i; // go up elseif (i == top + 1) { ++j; // shrink borders ++top, --bot, ++left, --right; } } else { throwstd::runtime_error("not behaved expectedly"); } } }
// abstraction for 2D point structPos { int x; int y; Pos(int xx, int yy): x(xx), y(yy) { } };
// abstraction for window structWindow { int id; Pos point; int width; int height; Window(int idx, int x, int y, int w, int h) : id(idx), point(x, y), width(w), height(h) { } };
/// is the point @c p in the window @c w boolIsInWindow(const Window& w, const Pos& p) { int left = w.point.x; int right = (left + w.width < WIDTH) ? (left + w.width) : WIDTH-1; int top = w.point.y; int bot = (top + w.height < HEIGHT) ? (top + w.height) : HEIGHT-1;
if (p.x >= left && p.x <= right) { if (p.y >= top && p.y <= bot) returntrue; } returnfalse; }
/// which window do i click on intClickWhich(vector<Window>& windows, const Pos& click) { // 窗口叠放次序,从上层到下层 for (int i = windows.size() - 1; i >= 0; --i) { if (IsInWindow(windows[i], click)) { int ret = windows[i].id + 1; windows.push_back(windows[i]); windows.erase(windows.begin() + i); return ret; } } return-1; }
intmain() { int num_open_windows, num_clicks; cin >> num_open_windows >> num_clicks; // read windows vector<Window> windows; for (int i = 0, x,y,w,h; i != num_open_windows; ++i) { cin >> x >> y >> w >> h; windows.emplace_back(i, x, y, w, h); } // read clicks vector<Pos> clicks; for (int i = 0, x,y; i != num_clicks; ++i) { cin >> x >> y; clicks.emplace_back(x, y); } // io done for (auto &e : clicks) { cout << ClickWhich(windows, e) << endl; }
return0; }
题九:Stern-Brocot Tree
网易雷火游戏2019校招
The Stern-Brocot tree is an infinite complete binary tree in which the verices correspond one-for-one to the positive rational numbers, whose values are ordered from the left to the right as in a search tree.
Figure above shows a part of the Stern-Brocot tree, which has the first 4 rows. The value in the node is the mediant of the left and right fractions. The mediant of two fractions A/B and C/D is defined as (A+C)/(B+D).
To construct the Stern-Brocot tree, we first define the left fraction of the root node is 0/1, and the right fraction of the root node is 1/0. So the value in the root node is the mediant of 0/1 and 1/0, which is (0+1)/(1+0)=1/1. Then the value of root node becomes the right fraction of the left child, and the left fraction of the right child. For example, the 1st node in row2 has 0/1 as its left fraction and 1/1(which is the value of its parent node) as its right fraction. So the value of the 1st node on row2 is (0+1)(1+1)=1/2. For the same reason, the value of the 2nd node in row2 is (1+1)/(1+0)=2/1. This construction progress goes on infinity. As a result, every positive rational number can be found on the Stern-Brocot tree, and can be found only once.
Give a rational nunmber in form P/Q, find the position of P/Q in the Stern-Borcot tree.
Input description: Input consists of two integers, P and Q (1<=P,Q <= 1000), which represent the rational number P/Q. We promise P and Q are relatively prime.
Output description: Output consists of two integers, R and C. R indicates the row index of P/Q in the Stern-Brocot tree, C indicates the index of P/Q in that row. Both R and C are base 1. We promise the position of P/Q is always in the first 12 rows of the Stern-Brocot tree, which means R<=12.
// decode pattern like "[3|abc]" // note: end is included stringPatternDecode(conststring& s, size_t beg, size_t end) { // the minmal len: [3|a] // beg=0, end=4 if (end - beg >= 4) { string ret; auto pos_delimiter = s.find('|', beg); int num_repeats = std::stoi(s.substr(beg+1, pos_delimiter-beg-1)); { auto pos_left = s.find('[', pos_delimiter); if (pos_left < end) // has sub-pattern { ret += s.substr(pos_delimiter+1, pos_left-pos_delimiter-1); auto pos_right = s.rfind(']', end-1); ret += PatternDecode(s, pos_left, pos_right); ret += s.substr(pos_right+1, end-pos_right-1); } else// has not sub-pattern { ret += s.substr(pos_delimiter+1, end-pos_delimiter-1); } } string res; while (num_repeats--) res += ret;
return res; } else { throwstd::invalid_argument("range too small"); } }
voidDecode(conststring& s, string* o) { if (s.empty()) throwstd::invalid_argument("empty coded string");
stack<size_t> stk; for (size_t i = 0; i != s.size(); ++i) { if (stk.empty() && std::isalpha(s[i])) { o->push_back(s[i]); } elseif (s[i] == '[') { stk.push(i); } elseif (s[i] == ']') { // if the stk has only one elem, then the last ']' of // a pattern is determined, we shall first decode this // pattern before going on. if (stk.size() == 1) { size_t pattern_beg = stk.top(); o->append(PatternDecode(s, pattern_beg, i)); } stk.pop(); } } }
// in-place merge sort namespace inplace { /// Merge two sorted range [beg, mid), [mid, end). voidMerge(vector<int>& arr, int beg, int mid, int end, int* invcnt) { for (int i = beg, j = mid; i < mid && j < end;) { if (arr[i] > arr[j]) { *invcnt += (mid - i); std::swap(arr[i], arr[j]); // rearrange to keep the structure for (int t = j; t > i+1; --t) { std::swap(arr[t], arr[t-1]); } ++mid; ++j; } ++i; } }
/// Inplace merge sort the range [beg, end). voidMergeSort(vector<int>& arr, int beg, int end, int* invcnt) { if (end - beg > 1) // need sort { int mid = (beg + end) / 2; MergeSort(arr, beg, mid, invcnt); MergeSort(arr, mid, end, invcnt); Merge(arr, beg, mid, end, invcnt); } } } // namespace inplace
/// reverse the arr by length k voidReverseBy(vector<int>& arr, int k) { for (auto beg = arr.begin(); beg != arr.end(); beg += k) std::reverse(beg, beg + k); }
/// compute elapsed time from start_time inlinedoubleTimeElapsed(clock_t start_time) { returnstatic_cast<double>(clock()-start_time) / CLOCKS_PER_SEC * 1000; }
/// Test wrapper voidUniTest(constvector<int>& numbers, constvector<int>& qs, bool inplace) { vector<int> dummynum(numbers); vector<int> buffer(numbers.size()); for (auto e : qs) { int invcount = 0; ReverseBy(dummynum, 1<<e); buffer.clear(); buffer.assign(dummynum.begin(), dummynum.end());
if (inplace) // use inplace merge sort inplace::MergeSort(buffer, 0, buffer.size(), &invcount); else mergesort(buffer, &invcount);
cout << invcount << endl; } }
intmain() { // io int n; cin >> n; vector<int> numbers; for (int i = 0, tmp = 0; i != (1<<n); ++i) { cin >> tmp; numbers.push_back(tmp); } int m; cin >> m; vector<int> qs; for (int i = 0, tmp = 0; i != m; ++i) { cin >> tmp; qs.push_back(tmp); } // done clock_t start = clock(); UniTest(numbers, qs, false); cout << TimeElapsed(start) << "ms for mergesort" << endl;
// print the right-most node of each layer of a tree. vector<int> PrintRightMost(TreeNode* root) { if (!root) throwstd::invalid_argument("empty tree"); vector<int> ret; while (root) { ret.push_back(root->val); // prefer see right child than left if (!root->right) root = root->left; root = root->right; } return ret; }
/* * 1. queue2 as a auxiliary storage * 2. push: push to the rear of queue1 * 3. pop: * 3.1. quque1 has only one element, pop it * 3.2. pop the elements of queue1, push them to queue2 * till queue1 has exactly one left, pop it * then pop elems of queue2 push to queue1 */
classSolution { vector<int> printListFromTailToHead(ListNode* head) { vector<int> ret; // boundary case if (head == nullptr) return ret;
stack<int> s; for (auto p = head; p != nullptr; p = p -> next) { s.push(p -> val); }
while (!s.empty()) { ret.push_back(s.top()); s.pop(); } return ret; }
// recursive needs a member variable // use recursion stack, tricky vector<int> arr; vector<int> reversePrint(ListNode* head) { if (head) { reversePrint(head -> next); arr.push_back(head -> val); } return arr; } /* * Consider the closure, at one recursive step, * what I should do? Let's drop all the details, * just look one recursive step. * What had I done? * Oh gee, I see if the head is not null, * I must push the value to the vector, * but before this, I should take a look at * `head -> next`, since I have to push * the tail first. So which one can help me * do this? Yes, the function itself! Then * after I have addressed the tail, now I'm * going to push current value to the vector. * That's all I need! * The key is you work in one recursive step, and * form a closure for the next, and do not forget * the base case (stopping rules). That how * recursion runs! And you are free of those * confusing details. */ };
/* * The idea is I push all elems of `pushV` into * a stack `s`, simultaneously, I track if the elems * of `popV` equal to the `s.top()`. If it is, I will * pop a elem from the stack and track the next elem * of `popV` till a mismatch. Then I'll continue to push * elems into the stack. * * When I run out all elems of `pushV`, I check if the * stack is empty, if it is, then the `popV` should be * a possible pop sequence or vice versa. * * Credit: https://www.nowcoder.com/profile/248554 */
while(!q.empty()) { auto cur = q.front(); // from left to right push the current // root's children to the queue if (cur -> left) q.push(cur -> left); if (cur -> right) q.push(cur -> right); ret.push_back(cur -> val); q.pop(); } return ret; }
/* * `n` and `n-1` are the same from high bit position * to low, they differs from the last bit of 1 of n, * let's name it x. Have a look from x to the lowest * bit position, * n: 010101000 * & x <--- position x * n-1: 010100111 * --------------- * 010100000 <--- n&(n-1) * It erases the last bit of 1 in n!!! * * This is why the following procedure will work. :) */
#include<iostream>
intNumOf1(int n){ int cnt = 0; while (n) { n = n & (n-1); ++cnt; } return cnt; }
intmain(){ int N = 0; std::cin >> N; std::cout << NumOf1(N); return0; }
不可描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有 1、10、11、12、13 因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
/* * Credits: unknown * * Idea: * 1) Focus exactly one decimal position, calculate the * number of ones. * 2) Run out from lowest to highest position, add them * together, it's the answer. * * Imaging the numbers from 1 to n sits in a line in * front of you. Now you're required to calculates all * the ones in the sequence. A basic way is that, each * time I focus on the same digit pos, and count all * ones in that pos. Next time I focus on another pos. * And I sum them all together, finally got the answer. * * See how can we count the num of ones in a specific * decimal pos? Let's do that! Suppose N = 301563. * * Step 1. * Now I focus the hundred position, and split N into `a` * and `b`, where * * a = ceil(N / 100) = 3015 * b = N % 100 = 63 * * 1). There are (a / 10 + 1) = 302 hits of one * 2). Each of length 100 * 3). Totally (a/10 + 1) * 100 hits of one * * Let me explain a little: * (000-301)5(00-99) -> (000-301)1(00-99) * * The digits above hundred pos have 302 variants, and * the digits under hundred pos has 100 variants, thus * gives a total (a/10 + 1) * 100. * * Step 2. * This time I focus on thousand pos, and now * * a = N / 1,000 = 301 * b = N % 1,000 = 563 * * 1). There are (a/10) = 30 hits of one * 2). Each of length 1000 * 3). And a tail hits of 564 * * (00-29)1(000-999) + 301(000-563) * This gives 30 * 1000 + 564 = (a/10)*1000+(b+1) * * Step 3. * Now move to ten thousand pos, with * * a = N / 10,000 = 30 * b = N % 10,000 = 1563 * * 1). There are total (a/10)=3 hit * 2). Each of length 10,000 * * (0-2)1(0000-9999) * gives 3 * 10,000 = (a/10). * * That's all 3 cases. Let's write! */
classSolution { public: intNumOnes(int n){ int ones = 0; for (longlong m = 1; m <= n; m *= 10) { ones += /* * this covers case 1 & 3 * since (a+8)/10 = a/10 + 1 if a%10 >= 2 * (a+8)/10 = a/10 if a%10 == 0 */ (n/m + 8) / 10 * m + // case 2 (n/m % 10 == 1) * (n%m + 1); } return ones; } };
Given an array, rotate the array to the right by k steps, where k is non-negative.
Example 1:
1 2 3 4 5 6
Input: [1,2,3,4,5,6,7] and k = 3 Output: [5,6,7,1,2,3,4] Explanation: rotate 1 steps to the right: [7,1,2,3,4,5,6] rotate 2 steps to the right: [6,7,1,2,3,4,5] rotate 3 steps to the right: [5,6,7,1,2,3,4]
Example 2:
1 2 3 4 5
Input: [-1,-100,3,99] and k = 2 Output: [3,99,-1,-100] Explanation: rotate 1 steps to the right: [99,-1,-100,3] rotate 2 steps to the right: [3,99,-1,-100]
Note:
Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.