• LeetCode Weekly Contest 28


    1. 551. Student Attendance Record I

    2. 552. Student Attendance Record II

    hihocode原题,https://hihocoder.com/contest/offers10/problems

    直接粘贴代码。

    其实这个第二题的问题挺多的,这里还是写一下分析过程。

    我刚开始做的时候,考虑也是计数dp,一维记录a的个数,一维记录末尾l的个数,然后考虑转移,后来好像是因为转移情况复杂,简化为不考虑a的个数,因为a的个数只有1个,所以可以单独考虑,然后考虑只有p和l的情况,这样的问题应该是很好考虑的,我写的有点

    复杂,我是记录末尾是什么,其实只用考虑末尾l的个数就行,只用一维就可以解决,而我使用了2维,枚举最后的2个字符是什么,还是比较繁琐的。计算过程,就是状态的转移过程,很好理解。

    接下来考虑a的情况,最后的结果肯定先要加上没有a的情况,然后枚举a的位置,然后左右2边就又变成上面没a的情况,把左和右乘起来,就是最后的结果。

    我的做法不具有通用性,而且绕的弯子有点大,没有抓住问题的本质。

    接下来考虑正常的做法,计数dp,一维记录a的个数,一维记录末尾l的个数,然后考虑转移的情况。

    转移的详细分析:http://bookshadow.com/weblog/2017/04/16/leetcode-student-attendance-record-ii/

    其实自己手动分析,其实也是很简单的。

    注意:上面的考虑的是,谁可以转移到我,这是一个收集的过程,分析可能的情况,而且情况数目很多,代码量大,很容易出错

    解法二:

     1 #include<bits/stdc++.h>
     2 #define pb push_back
     3 typedef long long ll;
     4 using namespace std;
     5 typedef pair<int, int> pii;
     6 const int maxn = 1e5 + 10;
     7 const int mod = 1e9 + 7;
     8 ll dp[maxn][2][3];
     9 int n;
    10 void add(ll& x, ll y) {
    11     x = (x + y) % mod;
    12 }
    13 void solve() {
    14     cin >> n;
    15     dp[0][0][0] = 1;
    16     for (int i = 1; i <= n; i++) {
    17         add(dp[i][0][0], dp[i - 1][0][0]);
    18         add(dp[i][0][0], dp[i - 1][0][1]);
    19         add(dp[i][0][0], dp[i - 1][0][2]);
    20 
    21 
    22         add(dp[i][0][1], dp[i - 1][0][0]);
    23         add(dp[i][1][1], dp[i - 1][1][0]);
    24 
    25         add(dp[i][0][2], dp[i - 1][0][1]);
    26         add(dp[i][1][2], dp[i - 1][1][1]);
    27 
    28         add(dp[i][1][0], dp[i - 1][1][0]);
    29         add(dp[i][1][0], dp[i - 1][1][1]);
    30         add(dp[i][1][0], dp[i - 1][1][2]);
    31         add(dp[i][1][0], dp[i - 1][0][0]);
    32         add(dp[i][1][0], dp[i - 1][0][1]);
    33         add(dp[i][1][0], dp[i - 1][0][2]);
    34 
    35     }
    36     ll res = 0;
    37     for (int i = 0; i < 2; i++)
    38         for (int j = 0; j < 3; j++)
    39             add(res, dp[n][i][j]);
    40     cout << res << endl;
    41 }
    42 
    43 int main() {
    44     freopen("test.in", "r", stdin);
    45     //freopen("test.out", "w", stdout);
    46     ios::sync_with_stdio(0);
    47     cin.tie(0); cout.tie(0);
    48     solve();
    49     return 0;
    50 }
    View Code

    转移复杂,需要考虑的情况多。

    然后我参考排行榜第一名的代码,就有下面的分析:

    所以就有下面的分析:其实应该考虑的是:我可以转移到谁,这是一个分发的过程,因为当前字符可能为a,p, l,所以是很好考虑的,代码也相对好写些

    解法三:

     1 #include<bits/stdc++.h>
     2 #define pb push_back
     3 typedef long long ll;
     4 using namespace std;
     5 typedef pair<int, int> pii;
     6 const int maxn = 1e5 + 10;
     7 const int mod = 1e9 + 7;
     8 ll dp[maxn][2][3];
     9 int n;
    10 void add(ll& x, ll y) {
    11     x = (x + y) % mod;
    12 }
    13 void solve() {
    14     cin >> n;
    15     dp[0][0][0] = 1;
    16     for (int i = 0; i < n; i++) {
    17         for (int x = 0; x < 2; x++) {
    18             for (int y = 0; y < 3; y++) {
    19                 ll cur = dp[i][x][y];
    20                 if(cur == 0) continue;
    21                 add(dp[i + 1][x][0], cur);
    22                 if(x + 1 < 2) {
    23                     add(dp[i + 1][x + 1][0], cur);
    24                 }
    25                 if(y + 1 < 3)
    26                     add(dp[i + 1][x][y + 1], cur);
    27             }
    28         }
    29     }
    30     ll res = 0;
    31     for (int i = 0; i < 2; i++)
    32         for (int j = 0; j < 3; j++)
    33             add(res, dp[n][i][j]);
    34     cout << res << endl;
    35 }
    36 
    37 int main() {
    38     freopen("test.in", "r", stdin);
    39     //freopen("test.out", "w", stdout);
    40     ios::sync_with_stdio(0);
    41     cin.tie(0); cout.tie(0);
    42     solve();
    43     return 0;
    44 }
    View Code

    上面的代码都可以采用滚动数组优化。

    其实分析到这里就差不多了,更详细的做法是贴出代码,一步一步解释,但是那样实在是太麻烦了,许多事情还是需要自己去走一遍的。

    上面算是3种方法,第三种是最好的。

    这个题目的n的限制为1e5,其实如果为1e8, 可以做么,其实是可以的,搞出来一个转移矩阵,然后快速幂来解决的,这也算是通用的套路吧。

    转移矩阵的写法:

     1 #include<bits/stdc++.h>
     2 #define pb push_back
     3 typedef long long ll;
     4 using namespace std;
     5 typedef pair<int, int> pii;
     6 const int maxn = 1e3 + 10;
     7 const int mod = 1e9 + 7;
     8 struct mat {
     9     ll a[6][6];
    10     mat() {
    11         memset(a, 0, sizeof a);
    12     }
    13     void pr() {
    14         for (int i = 0; i < 6; i++) {
    15             for (int j = 0; j < 6; j++)
    16                 cout << a[i][j] << " ";
    17             cout << endl;
    18         }
    19     }
    20 };
    21 mat mul(mat& a, mat& b) {
    22     mat c;
    23     for (int i = 0; i < 6; i++) {
    24         for (int j = 0; j < 6; j++) {
    25             for (int k = 0; k < 6; k++) {
    26                 c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j] % mod) % mod;
    27             }
    28         }
    29     }
    30     return c;
    31 }
    32 mat tr, one;
    33 int f(int x, int y) {
    34     return x * 3 + y;
    35 }
    36 void init() {
    37     for (int i = 0; i < 2; i++) {
    38         for (int j = 0; j < 3; j++) {
    39             int cur = f(i, j);
    40             one.a[cur][cur] = 1;
    41             tr.a[cur][f(i, 0)] = 1;
    42             if(i + 1 < 2)
    43                 tr.a[cur][f(i + 1, 0)] = 1;
    44             if(j + 1 < 3)
    45                 tr.a[cur][f(i, j + 1)] = 1;
    46         }
    47     }
    48 
    49 }
    50 int n;
    51 void solve() {
    52     cin >> n;
    53     init();
    54     //tr.pr();
    55     //one.pr();
    56     mat res;
    57     res.a[0][0] = 1;
    58     while(n) {
    59         if(n & 1) one = mul(one, tr);
    60         n >>= 1;
    61         tr = mul(tr, tr);
    62     }
    63     res = mul(res, one);
    64     ll r = 0;
    65     for (int i = 0; i < 6; i++)
    66         r = (r + res.a[0][i]) % mod;
    67     cout << r << endl;
    68 }
    69 
    70 int main() {
    71     freopen("test.in", "r", stdin);
    72     //freopen("test.out", "w", stdout);
    73     ios::sync_with_stdio(0);
    74     cin.tie(0); cout.tie(0);
    75     solve();
    76     return 0;
    77 }
    View Code

    相关的题目:

    https://hihocoder.com/contest/offers13/problem/4  需要搞出状态转移方程,然后快速幂解决。

    https://codejam.withgoogle.com/codejam/contest/10214486/dashboard#s=p3 这个难度比较高,也是转移方程的构造。

    就这样吧,写了这么多啰嗦的话,本来只是想贴一下自己写的。

    同时也暴露出一个问题,题目不是自己ac就完了,需要看一下别人是怎么做的,怎么想的,有没有什么巧妙的思路,这个过程才是关键的,这样才能提高和进步。

     1 class Solution {
     2 public:
     3 bool checkRecord(string s) {
     4     int n = s.size();
     5     int a = 0;
     6     for (int i = 0; i < n; i++) {
     7         if(s[i] == 'A') {
     8             a++;
     9             if(a > 1) return 0;
    10         }
    11         if(s[i] == 'L') {
    12             if(i + 2 < n && s[i + 1] == 'L' && s[i + 2] == 'L')
    13                 return 0;
    14         }
    15     }
    16     return 1;
    17 }
    18 };
    View Code
     1 typedef long long ll;
     2 const int maxn = 1e5 + 10;
     3 const int mod = 1e9 + 7;
     4 ll dp[maxn][2][2];
     5 ll s[maxn];
     6 class Solution {
     7 public:
     8 int checkRecord(int n) {
     9     if(n == 1)  {
    10         //cout << 3 << endl;
    11         return 3;
    12     }
    13     if(n == 2) {
    14         //cout << 8 << endl;
    15         return 8;
    16     }
    17     // O L A
    18     // 0 1 2
    19     memset(s, 0, sizeof s);
    20     memset(dp, 0, sizeof dp);
    21     for (int i = 0; i < 2; i++)
    22         for (int j = 0; j < 2; j++)
    23             dp[2][i][j] = 1;
    24     for (int c = 3; c <= n; c++) {
    25         for (int i = 0; i < 2; i++) {
    26             for (int j = 0; j < 2; j++) {
    27                 for (int x = 0; x < 2; x++) {
    28                     if(i == 1 && j == 1 && x == 1) {
    29                         continue;
    30                     }
    31                     dp[c][j][x] = (dp[c][j][x] + dp[c - 1][i][j]) % mod;
    32                 }
    33             }
    34         }
    35         for (int i = 0; i < 2; i++) {
    36             for (int j = 0; j < 2; j++) {
    37                 s[c] = (s[c] + dp[c][i][j]) % mod;
    38             }
    39         }
    40     }
    41     ll res = 0;
    42     res = s[n];
    43     s[0] = 1;
    44     s[1] = 2;
    45     s[2] = 4;
    46     for (int i = 1; i <= n; i++) {
    47         int left = i - 1, right = n - i;
    48         res = (res + s[left] * s[right] % mod) % mod;
    49     }
    50     return res;
    51 
    52 }
    53 };
    View Code

    3. 553. Optimal Division

    刚开始,看到长度为10,以为是暴力枚举,因为枚举每一个位置的优先级,然后计算,但是仔细想想,要是实现起来还是比较麻烦的。考虑到是第二题,不会这么麻烦,考虑

    数学的分析方法,结果最大,除数最小,什么时候最小,当然是连除,越除越小,然后答案就很明显了。直接输出结果。

     1 class Solution {
     2 public:
     3 string optimalDivision(vector<int>& nums) {
     4     int n = nums.size();
     5     stringstream ss;
     6     if(n == 1) {
     7         ss << nums[0];
     8     } else if(n == 2) {
     9         ss << nums[0] << "/" << nums[1];
    10     } else {
    11         ss << nums[0] << "/(" << nums[1];
    12         for (int i = 2; i < n; i++)
    13             ss << "/" << nums[i];
    14         ss << ")";
    15     }
    16     return ss.str();
    17 }
    18 };
    View Code

    4. 555. Split Concatenated Strings

    说实话,看完真的一点想法也没有,完全懵逼。

    静下心来分析,考虑整个字符串长度为1000,那么可以简单的枚举每一个字符串的开始,每一个字符都有可能作为开头,接下来考虑,固定头部的时候,其他的字符串怎么办,其实是很明显的,因为其他字符串只有顺序和逆序2种情况,而且要使得结果最大,肯定选择2种里面较大的那一种,显然是确定的,所以枚举的每一个起始字符以后,可以在O(n)的复杂度内,计算出这个串,然后不断的更新结果,就可以了。

     1 class Solution {
     2 public:
     3 string splitLoopedString(vector<string>& v) {
     4     int n = v.size();
     5     if(n == 0) return "";
     6     string res;
     7     for (string&t : v) {
     8         string td = t;
     9         reverse(td.begin(), td.end());
    10         if(td > t)
    11             t = td;
    12         res += t;
    13     }
    14     //cout << res << endl;
    15     for (int i = 0; i < n; i++) {
    16         int sz = v[i].size();
    17         for (int j = 0; j < sz; j++) {
    18             string ml = v[i].substr(j, sz - j), mr = v[i].substr(0, j);
    19             string rl = v[i].substr(j + 1, sz - j - 1), rr = v[i].substr(0, j + 1);
    20             reverse(rl.begin(), rl.end());
    21             reverse(rr.begin(), rr.end());
    22             // ml   mr
    23             // rr rl
    24             for (int j = (i + 1) % n; j != i; j = (j + 1) % n) {
    25                 ml += v[j];
    26                 rr += v[j];
    27             }
    28 
    29             ml += mr;
    30             rr += rl;
    31             //cout << ml << endl;
    32             //cout << rr << endl;
    33             res = max(res, max(ml, rr));
    34         }
    35     }
    36     return res;
    37 }
    38 };
    View Code
  • 相关阅读:
    Echarts、大屏动态折线图无状态加载
    地图获取城市编码vue-jsonp请求,处理跨域
    将数组中的元素拼接为一个字符串
    vue兄弟组件通信、兄弟组件传值(本人是用的是中央控件作为媒介),并一同解决重负传值、第一未传值
    vue、react动态新增行
    vue-antd 分页pagination相关重置问题
    关闭ESLint警告,本人使用的是vue-antd-pro
    文本长短不一文本长短不一致,css让其左右两侧对齐致,css让其左右两侧对齐
    Maven私服搭建 Nexus
    Maven常用命令
  • 原文地址:https://www.cnblogs.com/y119777/p/6747228.html
Copyright © 2020-2023  润新知