赞
踩
502. IPO
- 题意:给定k,w,profits数组和capital数组。k表示最多可完成的任务数。w是初始资本。profits是各个任务的收益。capital是开始此任务需要的初始资金。每次完成一个任务后,将收益加到资本中。问最多可以达到多少资本。
- 题解:贪心。用优先队列。每次选取可以开始的任务中收益最大的。
- class Solution {
- public:
-
- struct pc{
- int profits;
- int capital;
- pc(int u,int v):profits(u),capital(v){}
-
- bool operator < (const Solution::pc &b) const {
- return profits < b.profits;
- }
- };
- struct cmp{
- bool operator()(struct pc &a,struct pc &b)const {
- if(a.capital != b.capital) return a.capital < b.capital;
- return a.profits > b.profits;
- }
- };
- int findMaximizedCapital(int k, int W, vector<int>& Profits, vector<int>& Capital) {
- vector<pc> v;
- for(int i = 0;i < Profits.size(); ++i){
- v.push_back(pc(Profits[i],Capital[i]));
- }
- sort(v.begin(),v.end(),cmp());
-
- priority_queue<pc> Q;
- int pos = 0;
- while(pos < v.size() && v[pos].capital <= W){
- Q.push(v[pos++]);
- }
- while(!Q.empty() && k--){
- pc u = Q.top(); Q.pop();
- W += u.profits;
- while(pos < v.size() && v[pos].capital <= W){
- Q.push(v[pos++]);
- }
- }
-
- return W;
- }
- };
514. Freedom Trail
- 题意:给定一个密码环,环上有一些小写字母。通过转动环,然后按按钮输入正确密码才能将门打开。密码是一个字符串。环上的字符以一个字符串给出,按顺时针排列。可以沿着逆时针和顺时针转动,转动一格计一步。按按钮就将位于位置0的字符输入,并计一步。问最少多少步可以将密码输入。
- 题解:动态规划。设dp[i][j]表示key从i到结尾,环上字符现在位于j时,要输完密码至少得步数。那么要转动到key[i]字符所在位置k,则dp[i][j] = min{dp[i + 1][k]} + 1。而k只要枚举当前位置最接近的左右两个和key[i]相等的位置即可(由于是小写字母,这个可以在O(32 * ring.length())的时间预先计算出来。接着,由于dp[i]只用到dp[i + 1]的信息,所以可以滚动数组,空间复杂度降为O(Ring.size() + Key.size())
- class Solution {
- public:
- int findRotateSteps(string ring, string key) {
-
- int n = ring.size();
- int m = key.size();
- if(m == 0) return 0;
- vector<vector<int> > left(n,vector<int>(26)),right(n,vector<int>(26));
-
- vector<int> leftpos(26,-1),rightpos(26,-1);
- for(int i = 0;i < n; ++i){
- leftpos[ring[i] - 'a'] = i;
- rightpos[ring[n - i - 1] - 'a'] = n - i - 1;
- }
-
- for(int i = 0;i < n; ++i){
- leftpos[ring[i] - 'a'] = i;
- rightpos[ring[n - i - 1] - 'a'] = n - i - 1;
- for(int j = 0;j < 26; ++j){
- left[i][j] = leftpos[j];
- right[n - i - 1][j] = rightpos[j];
- }
- }
-
- vector<vector<int> > dp(2,vector<int>(n,0));
-
- for(int i = m - 1;i >= 0; --i){
- int c = key[i] - 'a';
- for(int j = 0;j < n; ++j){
- int L = left[j][c];
- int R = right[j][c];
- dp[i & 1][j] = min((j - L + n) % n + dp[1 - i & 1][L],(R - j + n) % n + dp[1 - i & 1][R]) + 1;
- }
- }
- return dp[0][0];
- }
- };
517. Super Washing Machines
- 题意:给定N堆硬币(可能为空),每次可以任意选择m堆,从每一堆中那走一个,放到隔壁堆中取。问至少多少次能使所有堆得硬币一样多。
- 题解:dp[i]表示前0到i堆至少多少次能把多余的硬币放到右边去,并且除最后一个,都达到了目标值。dp[i + 1] = dp[i] + L +R,其中L和R分别是要从i+1堆向左边和右边拿走的硬币数量。
- class Solution {
- public:
- int findMinMoves(vector<int>& machines) {
- int n = machines.size();
- if(n <= 1) return 0;
- int tot = 0,ans = 0;
- for(int i = 0;i < n; ++i) tot += machines[i];
- if(tot % n) return -1;
- int ave = tot / n;
- machines.push_back(ave);
- tot = 0;
- tot = machines[0] - ave;
-
- for(int i = 0;i < n; ++i){
- if(tot >= 0)
- ans = max(tot,ans);
- else{
- ans = max(ans,max(0,machines[i + 1] + tot - ave) - tot);
- }
- tot += machines[i + 1] - ave;
- machines[i + 1] += tot;
- }
- return ans;
- }
- };
546. Remove Boxes
- 题意:给定一个整数序列,每个数字表示颜色。每次可以取走若干个连续的同颜色的一段(长度为k),获得k*k的分数。问怎么取可以使得当序列取完获得的分数最大。
- 题解:动态规划。因为连续的肯定是要一起取走的,所以先合并。设dp[i][j][k]表示从位置i到位置j,前面剩下有k个和i相同颜色的位置,那么要么i和前面的合并掉取走,要么一起留下来,和下一个一样的合并。答案是dp[0][n - 1][0]。
- //vector会超内存
- class Solution {
- public:
-
- void merge(vector<int>& boxes,int n,vector<int>&color,vector<int>&length){
- boxes.push_back(-1);
- int curLength = 0;
- for(int i = 0;i < n; ++i){
- ++curLength;
- if(boxes[i] == boxes[i + 1]) continue;
- length.push_back(curLength);
- color.push_back(boxes[i]);
- curLength = 0;
- }
- }
-
- int removeBoxes(vector<int>& boxes) {
- int n = boxes.size();
- if(n <= 1) return n;
- vector<int> color,length;
-
- merge(boxes,n,color,length);
-
- n = color.size();
- if(n == 1) return length[0] * length[0];
- //vector<vector<vector<int>>> dp(n,vector<vector<int>>(n,vector<int>(boxes.size(),0)));
-
- int dp[100][100][100] = {0};
-
- return dfs(dp,color,length,0,n - 1,0);
- }
-
- int dfs(int dp[100][100][100],const vector<int>& color,vector<int>&length,int i,int j,int k){
- if(i > j) return 0;
- if(i == j) return (k + length[i]) * (k + length[i]);
- int &res = dp[i][j][k];
- if(res) return res;
-
- res = dfs(dp,color,length,i + 1,j,0) + (k + length[i]) * (k + length[i]);
- for(int p = i + 1;p <= j; ++p){
- if(color[p] != color[i]) continue;
- res = max(res, dfs(dp, color,length, i + 1, p - 1, 0) + dfs(dp, color,length, p, j, k + length[i]));
- }
-
-
- return res;
- }
-
-
- };
-
552. Student Attendance Record II
- 题意:给定三种字符(A,P,L)给定一个数字n,问长度n由这三种字符组成且的串的共有多少个。限制是其中一种只能有一个(A),还有一种最多两个连续(L),剩下一个没有限制(P)。
- 题解:dp[i][j][k]表示长度为i,A是否出现过,结尾连续L的个数为k的串的个数。然后进行转移即可。由于dp[i]只用到dp[i - 1]的信息,故可以用滚动数组。
- class Solution {
- public:
- const long long MOD = 1E9 + 7;
- int checkRecord(int n) {
- long long dp[2][2][3] = {0LL};
-
- dp[0][0][0] = 1LL;
-
- for(int i = 1;i <= n; ++i){
- for(int k = 0;k < 3; ++k){
- if(k > 0){
- dp[i&1][0][k] = dp[1 - i&1][0][k - 1];
- dp[i&1][1][k] = dp[1 - i&1][1][k - 1];
- }
- else{
- dp[i&1][0][k] = dp[1 - i&1][0][0] + dp[1 - i&1][0][1] + dp[1 - i&1][0][2];
- dp[i&1][1][k] = dp[1 - i&1][1][0] + dp[1 - i&1][1][1] + dp[1 - i&1][1][2] +
- dp[i&1][0][k];
- }
- dp[i&1][0][k] %= MOD;
- dp[i&1][1][k] %= MOD;
- }
-
- }
- return (dp[n&1][0][0] + dp[n&1][0][1] + dp[n&1][0][2] + dp[n&1][1][0] + dp[n&1][1][1] + dp[n&1][1][2]) % MOD;
- }
- };
564. Find the Closest Palindrome
- 题意:给定一个数字字符串,确定和它离最近的那个回文串,如果有两个一样近,输出小的那个。
- 题解:取这个字符串的前半段,如果它本身不是回文串,那么取前半段然后镜像构造一个回文串,这个肯定是距离最小的。如果它本身是回文串,将前半段减1,加1两种情况构造回文串。最近那个肯定是这两个中的一个,这个很显然,接着就是细节的问题了。
- class Solution {
- public:
-
- bool g(string a,string b){ //a > b? true: false;
- if(a == "inf") return true;
- if(b == "inf") return false;
- if(a.length() > b.length()) return true;
- if(a.length() < b.length()) return false;
-
- for(int i = 0;i < a.length(); ++i){
- if(a[i] > b[i]) return true;
- if(a[i] < b[i]) return false;
- }
- return false;
- }
-
- string dis(string a,string b){
- if(a == b) return "inf";
- if(g(a,b)) swap(a,b);
- a = string(b.length() - a.length(),'0') + a;
-
- char tmp = 0;
- string s = "";
- int i = 0;
- for(int i = a.length() - 1;i >= 0; --i){
- char t = b[i] - a[i] - tmp;
- if(t < 0) tmp = 1, t += 10;
- else tmp = 0;
- s = char('0' + t) + s;
- }
- int pos = 0 ;
- while(pos < s.length() && s[pos] == '0') ++pos;
- s = s.substr(pos);
-
- return s == string("0") ? "inf":s;
- }
- string minor(string s){
- int mid = (s.length() + 1) / 2;
- for(int i = mid; i < s.length() ; ++i)
- s[i] = s[s.length() - 1 - i];
- return s;
- }
- string nearestPalindromic(string s) {
- int pos = 0;
- while(pos < s.length() - 1 && s[pos] == '0') ++pos;
- s = s.substr(pos);
- int n = s.size();
-
- if(n <= 1){
- cout<<"true"<<endl;
- if(s == "") return "0";
- if(s == "0") return "1";
- cout<<"noew"<<(s[0] - 1)<<endl;
- return string("") + char(s[0] - 1);
- }
-
- string s1 = minor(s);
- string diff1 = dis(s,s1);
- int mid = (s.length() - 1) / 2;
- string s2 = s;
- while(mid >= 0 && s2[mid] == '0'){
- s2[mid] = '9';
- --mid;
- }
- if(mid == 0 && *(s2.begin()) == '1'){
- s2.erase(s2.begin());
- s2[(s2.length() - 1) / 2] = '9'; //this is because it's the closer one
- }
- else
- s2[mid] = s2[mid] - 1;
- s2 = minor(s2);
- string diff2 = dis(s,s2);
-
- mid = (s.length() - 1) / 2;
-
- string s3 = s;
- while(mid >= 0 && s3[mid] == '9'){
- s3[mid] = '0';
- --mid;
- }
- if(mid < 0)
- s3 = "1" + s3;
- else
- s3[mid] = s3[mid] + 1;
-
- s3 = minor(s3);
-
- string diff3 = dis(s,s3);
-
- if(!g(diff2,diff1) && !g(diff2,diff3)) return s2;
- if(!g(diff1,diff2) && !g(diff1,diff3)) return s1;
- return s3;
-
- }
- };
587. Erect the Fence
- 题意:就是求凸包,都是整点,可能有重复,也可以退化成一直线。从最左边开始,逆时针输出。
- 题解:Graham扫描
- /**
- * Definition for a point.
- * struct Point {
- * int x;
- * int y;
- * Point() : x(0), y(0) {}
- * Point(int a, int b) : x(a), y(b) {}
- * };
- */
- Point operator-(Point &a, Point &b) {
- int x = a.x - b.x;
- int y = a.y - b.y;
- return Point(x,y);
- }
- class Solution {
- public:
-
-
- struct cmp{
- bool operator()(const Point &a, const Point &b){
- if(a.x == b.x) return a.y < b.y;
- return a.x < b.x;
- }
- };
- bool eq(const Point &a, const Point &b){
- return a.x == b.x && a.y == b.y;
- }
-
- int cross(Point &a,Point &b){
- return a.x * b.y - a.y * b.x;
- }
-
-
- vector<int> convexHull(vector<Point>& points){
-
-
- int n = points.size();
- vector<int> S(n,0);
- int end = 0;
-
- for(int i = 1;i < n; ++i){
- if(end == 0){
- S[++end] = i;
- continue;
- }
- int top = S[end];
- int pre = S[end - 1];
- Point v1 = points[i] - points[pre];
- Point v2 = points[top] - points[pre];
- if(cross(v1,v2) > 0){
- --end;
- --i;
- }else{
- S[++end] = i;
- }
- }
- vector<int> down(S.begin(),S.begin() + end + 1);
-
- if(down.size() == points.size()) return down;
-
- S[0] = n - 1; end = 0;
- for(int i = n - 2;i >= 0; --i){
- if(end == 0){
- S[++end] = i;
- continue;
- }
- int top = S[end];
- int pre = S[end - 1];
- Point v1 = points[i] - points[pre];
- Point v2 = points[top] - points[pre];
- if(cross(v1,v2) > 0){
- --end;
- ++i;
- }else{
- S[++end] = i;
- }
- }
-
- vector<int>up(S.begin() + 1,S.begin() + end);
-
- down.insert(down.end(),up.begin(),up.end());
- return down;
- }
- /*
- int gcd(int a,int b){
- return b == 0 ? a : gcd(b, a % b);
- }
-
- bool inaLine(vector<Point>& points){
- int pos = 1;
- while(pos < points.size() && points[pos].x == points[0].x && points[pos].y == points[0].y) ++pos;
- if(pos == points.size()) return true;
-
- Point tmp = points[pos] - points[0];
-
- int x = tmp.x,y = tmp.y;
- int d = gcd(x,y);
- x /= d; y /= d;
- for(int i = 1;i < points.size(); ++i){
- int px = points[i].x - points[0].x, py = points[i].y - points[0].y;
- if(px == 0 && py == 0) continue;
- d = gcd(px,py);
- px /= d; py /= d;
- if(x != px || y != py) return false;
- }
-
-
- return true;
- }
- */
- vector<Point> outerTrees(vector<Point>& points) {
- if(points.size() <= 1) return points;
-
- sort(points.begin(),points.end(),cmp()); //left to right ,down to up
- //if(inaLine(points)) return points;
- vector<int> convex = convexHull(points);
- vector<Point> ans;
- for(auto pos:convex) ans.push_back(points[pos]);
- return ans;
- }
- };
591. Tag Validator
题意:验证一个标签是否合法。
标签以<tagname>起始</tagname终止>,tagname必须是大写字符,且长度不大于9。
形式如下<tagname>tagcontent </tagname>
tagcontent可能包含以下部分,
题解:模拟题。直接模拟判断过程。懒得写,见代码注释
- class Solution {
- public:
- bool isValid(string code) {
- if(code.empty() || code[0] != '<')
- return false;
- int index = 0, N = code.length();
- stack<string> tag;
-
- while(index < N){
- //遇到左括号,三种情况,起始,终止标签和CDATA
- if(code[index] == '<'){
- ++index;
- //CDATA部分
- if(index < N && code[index]== '!'){
- //不被包含在tag中,则invalid
- if(tag.empty())
- return false;
- ++index;
- //接下是[CDATA[
- if(code.substr(index, 7) != "[CDATA[")
- return false;
- index += 7;
- //CDATA_CONTENT
- while(true){
- //找到一个可能的CDATA_CONTENT结尾
- while(index < N && code[index] != ']') ++index;
- if(index < N - 3){
- if(code.substr(index,3) == "]]>") break;
- }else
- return false;
- ++index;
- }
- }else if(index < N && code[index]== '/'){ //tag结尾
- if(tag.empty())
- return false;
- ++index;
- int i = index, len;
- //标签,len为标签长度
- while(i < N && code[i] != '>'){
- len = i - index + 1;
- if(len > (int)tag.top().length() || code[i] != tag.top()[len - 1])
- return false;
- i++;
- }
- len = i - index;
- if(len != (int)tag.top().length())
- return false;
-
-
- tag.pop();
- index = ++i;
- //外层必须是一个tag,而不是两个tag并排
- if(tag.empty() && index != N)
- return false;
- }else{ //起始标签
- int i = index, len;
- //tag
- while(i < N && code[i] != '>'){
- len = i - index + 1;
-
- if(code[i] > 'Z' || code[i] < 'A')
- return false;
- i++;
- if(len > 9)
- return false;
- }
- len = i - index;
- if(i == N || len < 1 || len > 9)
- return false;
- tag.push(code.substr(index,len));
- index = ++i;
- }
- }else //其他内容。直接略
- ++index;
- }
- return tag.empty();
- }
- };
600. Non-negative Integers without Consecutive Ones
- 题意:给定一个数字n,问比它小的,且二进制没有连续1的非负数有多少个。
- 题解:注意到,二进制长度为k的没有连续1的非负数的是Fibonacci数。接着,对于一个n,我们分别计算前缀和它一样的那些数有多少,加起来即可。如果它本身也是,加上它本身。另外一种做法是动态规划。dp[i][j]表示前i位,最后一位为j且不大于num前缀的数目。
- class Solution {
- public:
-
- int findIntegers(int num) {
- int fib[31];
- fib[0] = 1;
- fib[1] = 2;
- for (int i = 2; i < 32; ++i)
- fib[i] = fib[i - 1] + fib[i - 2];
- int ans = 0, k = 30, pre_bit = 0;
- for(int k = 30;k >= 0; --k) {
- if (num & (1 << k)) {
- ans += fib[k];
- if (pre_bit) return ans;
- pre_bit = 1;
- }else
- pre_bit = 0;
- }
- return ans + 1;
- }
- };
- 动态规划解法,压缩空间:
- public class Solution {
- public int findIntegers(int num) {
- //one:all bit before cur is less than num and no continues 1 and cur bit is at one;
- //zero:all bit before cur is less than num and no continues 1 and cur bit is at zero;
- int one=0,zero=0,temp;
- boolean isNum=true;
- for(int i=31;i>=0;i--){
- temp=zero;
- zero+=one;
- one=temp;
- if(isNum&&((num>>i)&1)==1){
- zero+=1;
- }
- if(((num>>i)&1)==1&&((num>>i+1)&1)==1){
- isNum=false;
- }
- }
- return one+zero+(isNum?1:0);
- }
- }
629. K Inverse Pairs Array
- 题意:给定n,k。问由1 ~n组成的序列中逆序对为k的共有多少种情况
- 题解:动态规划。设dp[i][j]为,前面有1~i + 1的序列,逆序对为j的有多少种。那么dp[i][j]
- 可以由i + 1所在位置进行递推。dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] + ... dp[i - 1][max(0,j - i)]
- 和每次预先计算,故时间复杂度为O(nk)
- class Solution {
- public:
- const int MOD = 1E9 + 7;
- int getsum(int i,int j, vector<int> &sum)const {
- int pre = max(j - i,0) - 1;
- if(pre < 0) return sum[j];
- else return sum[j] - sum[pre];
- }
-
- int kInversePairs(int n, int k) {
- if(k == 0) return 1;
- vector<int> dp(k + 1,0);
- vector<int> sum(k + 1,1);
-
- dp[0] = 1;
-
- for(int i= 0;i < n; ++i){
- for(int j = 1;j <= k; ++j){
- dp[j] = (getsum(i,j,sum) % MOD + MOD) % MOD;
- }
- for(int j = 1;j <= k; ++j) sum[j] = (sum[j - 1] + dp[j]) % MOD;
- }
-
- return dp[k];
- }
- };
630. Course Schedule III
- 题意:给定一些课程,以(需要的时间,最迟结束时间)给出。课程之间不能重叠。问最多可以安排多少个课程。
- 题解:贪心。先对课程,按最迟结束时间从小到大排序。然后一个个安排。如果可以安排下,那么就放进去。如果安排不下,那么去掉前面(包括自己)一个耗时最多的。这样子肯定是最优的。因为对于前i个,如果这样做事最优的,那么对于第i+1个,这样做也肯定是最优的,不然得出矛盾。
- class Solution {
- public:
- struct cmp{
- bool operator ()(vector<int> &a, vector<int>&b){
- return a[1] < b[1];
- }
- };
- struct cmp2{
- bool operator ()(pair<int,int> &a,pair<int,int> &b){
-
- return a.first < b.first;
-
- }
- };
-
- int scheduleCourse(vector<vector<int>>& courses) {
-
- sort(courses.begin(),courses.end(),cmp());
- priority_queue<pair<int,int> ,vector<pair<int,int>>, cmp2>Q;
- int endTime = 0;
- for(int i = 0;i < courses.size(); ++i){
- Q.push(make_pair(courses[i][0],courses[i][1]));
- endTime += courses[i][0];
- if(endTime > courses[i][1]){
- endTime -= Q.top().first;
- Q.pop();
- }
- }
- return Q.size();
- }
- };
632. Smallest Range
- 题意:给定k个list,每个list是一个有序int数组。求一个最小的区间,使得这k个数组中每一个都至少存在一个数在这个区间中。
- 题解:先合并成一个pair数组(值和所属的list)。然后双指针遍历即可。
- class Solution {
- public:
- static bool cmp(pair<int,int> &a,pair<int,int> &b){
- return a.first > b.first;
- }
- vector<int> smallestRange(vector<vector<int>>& nums) {
- vector<pair<int,int>> numpair;
-
- int k = nums.size();
- for(int i = 0;i < k; ++i){
- vector<pair<int,int>> tmp;
- for(auto &num:nums[i]){
- tmp.push_back(make_pair(num,i));
- }
- numpair.resize(numpair.size() + tmp.size());
- merge(numpair.rbegin() + tmp.size(),numpair.rend(),tmp.rbegin(),tmp.rend(),numpair.rbegin(),cmp);
- }
-
- vector<int> counts(k);
- int visn = 0;
- int a = -1,b = -1,minimumS = INT_MAX;
- int pre = 0;
- for(int i = 0;i < numpair.size() ; ++i){
- if(++counts[numpair[i].second] == 1) ++visn;
-
- if(visn == k){
- while(counts[numpair[pre].second] > 1){
- --counts[numpair[pre].second];
- ++pre;
- }
- if(numpair[i].first - numpair[pre].first < minimumS){
- minimumS = numpair[i].first - numpair[pre].first;
- a = numpair[pre].first;
- b = numpair[i].first;
- }
- }
- }
- return vector<int>{a,b};
- }
- };
639. Decode Ways II
- 题意:字符A-Z能编码成1到26,给定一串编码,问可以解码成多少种情况。其中编码;*;可以代表1-9中任意字符。
- 题解:动态规划。dp[i]表示编码串前i个可以解码的种数。然后按当前字符和前一个字符的类别进行状态转移。
-
- class Solution {
- public:
- long long M = 1000000007;
- int numDecodings(string s) {
- int n = s.size();
- if(n == 0) return 1;
- vector<long long> dp(n + 1,0);
- dp[0] = 1;
- dp[1] = s[0] == '*'? 9 : s[0] != '0';
- if(dp[1] == 0) return 0;
-
- for(int i = 2; i <= n; ++i){
- if(s[i - 2] == '1'){
- if(s[i - 1] == '0') dp[i] = dp[i - 2];
- else if(s[i - 1] == '*') dp[i] = 9 * dp[i - 2] + dp[i - 1] * 9;
- else dp[i] = dp[i - 2] + dp[i - 1];
-
- }else if(s[i - 2] == '2'){
- if(s[i - 1] == '0') dp[i] = dp[i - 2];
- else if(s[i - 1] == '*') dp[i] = 6 * dp[i - 2] + dp[i - 1] * 9;
- else if(s[i - 1] <= '6' && s[i - 1] >='1') dp[i] = dp[i - 2] + dp[i - 1];
- else dp[i] = dp[i - 1];
-
- }else if(s[i - 2] == '*'){
- if(s[i - 1] == '0') dp[i] = 2 * dp[i - 2];
- else if(s[i - 1] == '*') dp[i] = 15 * dp[i - 2] + dp[i - 1] * 9;
- else if(s[i - 1] <= '6' && s[i - 1] >= '1') dp[i] = 2 * dp[i - 2] + dp[i - 1];
- else dp[i] = dp[i - 2] + dp[i - 1];
-
- }else if(s[i - 2] > '2'){ //s[i - 2] greater than 2
- if(s[i - 1] == '0') dp[i] = 0;
- else if(s[i - 1] == '*')
- dp[i] = 9 * dp[i - 1];
- else
- dp[i] = dp[i - 1];
- }else{ //0
- if(s[i - 1] == '0') dp[i] = 0;
- else dp[i] = s[i - 1] == '*'? 9 * dp[i - 1] : dp[i - 1];
- }
- dp[i] = dp[i] % M;
-
- }
- return dp[n];
-
- }
- };
644. Maximum Average Subarray II
- 题意:给定一个数组和一个数字k。问长度至少为k的连续段的最大平均值为多少。
- 题解:一个简单的做法就是二分答案,然后check。
- (nums[i]+nums[i+1]+...+nums[j])/(j-i+1)>=x
- =>nums[i]+nums[i+1]+...+nums[j]>=x*(j-i+1)
- =>(nums[i]-x)+(nums[i+1]-x)+...+(nums[j]-x)>=0
- 相当于检查有没有连续段长度至少为k且和非负。也就是最大k子段和,用单调队列动态规划解决。
- 算法复杂度为O(nlogn)。但这不是最优的,利用计算几何的方法,可以达到O(n)。
- 首先,计算前缀和P[i]。然后i到j的平均值为(P[j] - P[i - 1]) / (j - i + 1)。
- 我们把它看成两个点(i - 1,P[i - 1]),(j,P[j]),显然平均值就是他们两个点的线段斜率。
- 接着。我们从左至右,维护一个下凸包,每次就是找凸包的下切线。具体图解见:
- http://www.csie.ntnu.edu.tw/~u91029/MaximumSubarray.html
- 该问题的一个增强版本是加上限宽度。也就是在单调队列里面加一个操作即可
-
- class Solution {
- public:
- /*
- bool check(vector<int>& nums, int k,double sum){
- double cur = 0,pre = 0;
-
- for(int i = 0;i < nums.size(); ++i){
- cur += nums[i] - sum;
- if(i >= k) pre += nums[i - k] - sum;
- if(pre < 0) cur -= pre, pre = 0;
- if(i >= k - 1 && cur >= 0) return true;
- }
- return cur >= 0;
- }
-
- double binarySearchSolution(vector<int>& nums, int k){
- double eps = 1e-6;
- double left = INT_MIN,right = INT_MAX,mid;
- while(right - left > eps){
- mid = (left + right) / 2;
- if(check(nums,k,mid)) left = mid;
- else right = mid;
- }
- return right;
- }
- */
-
- double slope(int x,int y, vector<int> &presum){ // the slope of the x,y
- return double(presum[y + 1] - presum[x]) / (y + 1 - x);
- }
-
-
- double convexHullSolution(vector<int>& nums, int k){
- deque<int> Q;
- vector<int> presum(nums.size() + 1);
- presum[0] = 0;
- double ans = INT_MIN;
- for(int i = 0;i < nums.size(); ++i) presum[i + 1] = presum[i] + nums[i];
- for(int i = k - 1;i < nums.size(); ++i){
- while(Q.size() > 1 && slope(*(Q.end() - 2),Q.back() - 1,presum) >= slope(*(Q.end() - 2),i - k,presum)) Q.pop_back(); //update the convex
- Q.push_back(i - k + 1);
- while(Q.size() > 1 && slope(Q.front(),Q.at(1) - 1,presum) <= slope(Q.front(),i,presum)) Q.pop_front();
- ans = max(ans,slope(Q.front(),i,presum));
-
- }
- return ans;
- }
-
- double findMaxAverage(vector<int>& nums, int k) {
- //return binarySearchSolution(nums,k);
- return convexHullSolution(nums,k);
- }
- };
664. Strange Printer
题意:有一个stange printer他能做以下的事
给定一个字符串,问至少需要多少次打印才能得到。比如aba需要两次。先打印aaa,再打印b
题解:动态规划,设dp[i][j]表示i到j子串需要多少次打印。则有以下的情况
首先注意到,第一个字符一定可以在第一轮打印。如果它单独打印显然可以,否则,它和到另外的一个位置k的子串打印,那么显然也可以放在第一次,因为如果不是第一次,那么会覆盖掉前面的打印,把前面的打印的起始位置移到k以后并不会产生差别。所以我们可以分两种情况
- class Solution {
- int dp[101][101];
-
- int dfs(int i, int j, string &str){
- if(i > j) return 0;
- if(i == j) return 1;
- if(dp[i][j]) return dp[i][j];
-
- dp[i][j] = dfs(i + 1, j, str) + 1;
- for(int k = i + 1; k < str.length(); ++k){
- if(str[k] == str[i]) dp[i][j] = min(dp[i][j], dfs(i, k - 1, str) + dfs(k + 1, j, str));
- }
-
- return dp[i][j];
- }
-
- public:
- int strangePrinter(string s) {
- string str = "";
- for(int i = 0;i < s.length(); ++i){
- str += s[i];
- while(i + 1 < s.length() && s[i + 1] == s[i]) ++i;
- }
- return dfs(0, str.length() - 1, str);
- }
- };
668. Kth Smallest Number in Multiplication Table
题意:给定一个m *n的乘法表,求里面第k大的数
题解:二分答案mid,然后判断小于等于mid的有多少对。这个通过枚举第一个乘数就知道了。假设乘数为i,则其中小于等于mid的数为min(n, mid / i)个。(用双指针可能更快点,因为除法比较耗时,而++运算--运算是很快的)
- class Solution {
- public:
- int findKthNumber(int m, int n, int k) {
-
- int left = 1, right = n * m;
-
- while(left <= right){
- int mid = (left + right) >> 1;
-
- int num = (mid / n) * n;
- for(int i = mid / n + 1; i <= min(m, mid); ++i){
- num += mid / i;
- }
-
- if(num < k){
- left = mid + 1;
- }else{
- right = mid - 1;
- }
- }
- return right + 1;
- }
- };
675. Cut Off Trees for Golf Event
题意:给以个网格,表示一个森林,其中0表示障碍物不能通过,其他都能通过,1表示平地,高于1表示有一棵树。从(0,0)位置开始,每次砍最低的树,最少需要多少步
题解:按树高排序,就是从当前位置走到下个树的高度位置的最少步数,bfs可解决。一开始我看成树是不能通过的,只有砍掉才能通过,这样也可以做,稍微修改即可(将排序部分改成优先队列)。
- class Solution {
- typedef pair<int, pair<int,int>> height_pos, step_pos;
- //放在外面会快很多
- bool vis[50][50];
- queue<step_pos> Q;
-
- int get_step(const pair<int,int> &s, const pair<int,int> &e, const vector<vector<int>>& forest){
- while(not Q.empty()) Q.pop();
- Q.push(make_pair(0, s));
- //vector<vector<bool>> vis(forest.size(), vector<bool>(forest[0].size(), false));
- memset(vis, 0, sizeof(vis));
-
- int dir_x[] = {1, 0, -1, 0};
- int dir_y[] = {0, 1, 0, -1};
- vis[s.first][s.second] = true;
- while(not Q.empty()){
- step_pos cur = Q.front(); Q.pop();
- int x = cur.second.first, y = cur.second.second;
- if(cur.second == e) return cur.first;
-
- for(int d = 0; d < 4; ++d){
- int next_x = x + dir_x[d];
- int next_y = y + dir_y[d];
- if(next_x < 0 or next_y < 0 or next_x >= forest.size() or next_y >=
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。