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/。
其实自己手动分析,其实也是很简单的。
注意:上面的考虑的是,谁可以转移到我,这是一个收集的过程,分析可能的情况,而且情况数目很多,代码量大,很容易出错。
解法二:
#include<bits/stdc++.h>
#define pb push_back
typedef long long ll;
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e5 + ;
const int mod = 1e9 + ;
ll dp[maxn][][];
int n;
void add(ll& x, ll y) {
x = (x + y) % mod;
}
void solve() {
cin >> n;
dp[][][] = ;
for (int i = ; i <= n; i++) {
add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]); add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]); add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]); add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]);
add(dp[i][][], dp[i - ][][]); }
ll res = ;
for (int i = ; i < ; i++)
for (int j = ; j < ; j++)
add(res, dp[n][i][j]);
cout << res << endl;
} int main() {
freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
ios::sync_with_stdio();
cin.tie(); cout.tie();
solve();
return ;
}
转移复杂,需要考虑的情况多。
然后我参考排行榜第一名的代码,就有下面的分析:
所以就有下面的分析:其实应该考虑的是:我可以转移到谁,这是一个分发的过程,因为当前字符可能为a,p, l,所以是很好考虑的,代码也相对好写些。
解法三:
#include<bits/stdc++.h>
#define pb push_back
typedef long long ll;
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e5 + ;
const int mod = 1e9 + ;
ll dp[maxn][][];
int n;
void add(ll& x, ll y) {
x = (x + y) % mod;
}
void solve() {
cin >> n;
dp[][][] = ;
for (int i = ; i < n; i++) {
for (int x = ; x < ; x++) {
for (int y = ; y < ; y++) {
ll cur = dp[i][x][y];
if(cur == ) continue;
add(dp[i + ][x][], cur);
if(x + < ) {
add(dp[i + ][x + ][], cur);
}
if(y + < )
add(dp[i + ][x][y + ], cur);
}
}
}
ll res = ;
for (int i = ; i < ; i++)
for (int j = ; j < ; j++)
add(res, dp[n][i][j]);
cout << res << endl;
} int main() {
freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
ios::sync_with_stdio();
cin.tie(); cout.tie();
solve();
return ;
}
上面的代码都可以采用滚动数组优化。
其实分析到这里就差不多了,更详细的做法是贴出代码,一步一步解释,但是那样实在是太麻烦了,许多事情还是需要自己去走一遍的。
上面算是3种方法,第三种是最好的。
这个题目的n的限制为1e5,其实如果为1e8, 可以做么,其实是可以的,搞出来一个转移矩阵,然后快速幂来解决的,这也算是通用的套路吧。
转移矩阵的写法:
#include<bits/stdc++.h>
#define pb push_back
typedef long long ll;
using namespace std;
typedef pair<int, int> pii;
const int maxn = 1e3 + ;
const int mod = 1e9 + ;
struct mat {
ll a[][];
mat() {
memset(a, , sizeof a);
}
void pr() {
for (int i = ; i < ; i++) {
for (int j = ; j < ; j++)
cout << a[i][j] << " ";
cout << endl;
}
}
};
mat mul(mat& a, mat& b) {
mat c;
for (int i = ; i < ; i++) {
for (int j = ; j < ; j++) {
for (int k = ; k < ; k++) {
c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j] % mod) % mod;
}
}
}
return c;
}
mat tr, one;
int f(int x, int y) {
return x * + y;
}
void init() {
for (int i = ; i < ; i++) {
for (int j = ; j < ; j++) {
int cur = f(i, j);
one.a[cur][cur] = ;
tr.a[cur][f(i, )] = ;
if(i + < )
tr.a[cur][f(i + , )] = ;
if(j + < )
tr.a[cur][f(i, j + )] = ;
}
} }
int n;
void solve() {
cin >> n;
init();
//tr.pr();
//one.pr();
mat res;
res.a[][] = ;
while(n) {
if(n & ) one = mul(one, tr);
n >>= ;
tr = mul(tr, tr);
}
res = mul(res, one);
ll r = ;
for (int i = ; i < ; i++)
r = (r + res.a[][i]) % mod;
cout << r << endl;
} int main() {
freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
ios::sync_with_stdio();
cin.tie(); cout.tie();
solve();
return ;
}
相关的题目:
https://hihocoder.com/contest/offers13/problem/4 需要搞出状态转移方程,然后快速幂解决。
https://codejam.withgoogle.com/codejam/contest/10214486/dashboard#s=p3 这个难度比较高,也是转移方程的构造。
就这样吧,写了这么多啰嗦的话,本来只是想贴一下自己写的。
同时也暴露出一个问题,题目不是自己ac就完了,需要看一下别人是怎么做的,怎么想的,有没有什么巧妙的思路,这个过程才是关键的,这样才能提高和进步。
class Solution {
public:
bool checkRecord(string s) {
int n = s.size();
int a = ;
for (int i = ; i < n; i++) {
if(s[i] == 'A') {
a++;
if(a > ) return ;
}
if(s[i] == 'L') {
if(i + < n && s[i + ] == 'L' && s[i + ] == 'L')
return ;
}
}
return ;
}
};
typedef long long ll;
const int maxn = 1e5 + ;
const int mod = 1e9 + ;
ll dp[maxn][][];
ll s[maxn];
class Solution {
public:
int checkRecord(int n) {
if(n == ) {
//cout << 3 << endl;
return ;
}
if(n == ) {
//cout << 8 << endl;
return ;
}
// O L A
// 0 1 2
memset(s, , sizeof s);
memset(dp, , sizeof dp);
for (int i = ; i < ; i++)
for (int j = ; j < ; j++)
dp[][i][j] = ;
for (int c = ; c <= n; c++) {
for (int i = ; i < ; i++) {
for (int j = ; j < ; j++) {
for (int x = ; x < ; x++) {
if(i == && j == && x == ) {
continue;
}
dp[c][j][x] = (dp[c][j][x] + dp[c - ][i][j]) % mod;
}
}
}
for (int i = ; i < ; i++) {
for (int j = ; j < ; j++) {
s[c] = (s[c] + dp[c][i][j]) % mod;
}
}
}
ll res = ;
res = s[n];
s[] = ;
s[] = ;
s[] = ;
for (int i = ; i <= n; i++) {
int left = i - , right = n - i;
res = (res + s[left] * s[right] % mod) % mod;
}
return res; }
};
3. 553. Optimal Division
刚开始,看到长度为10,以为是暴力枚举,因为枚举每一个位置的优先级,然后计算,但是仔细想想,要是实现起来还是比较麻烦的。考虑到是第二题,不会这么麻烦,考虑
数学的分析方法,结果最大,除数最小,什么时候最小,当然是连除,越除越小,然后答案就很明显了。直接输出结果。
class Solution {
public:
string optimalDivision(vector<int>& nums) {
int n = nums.size();
stringstream ss;
if(n == ) {
ss << nums[];
} else if(n == ) {
ss << nums[] << "/" << nums[];
} else {
ss << nums[] << "/(" << nums[];
for (int i = ; i < n; i++)
ss << "/" << nums[i];
ss << ")";
}
return ss.str();
}
};
4. 555. Split Concatenated Strings
说实话,看完真的一点想法也没有,完全懵逼。
静下心来分析,考虑整个字符串长度为1000,那么可以简单的枚举每一个字符串的开始,每一个字符都有可能作为开头,接下来考虑,固定头部的时候,其他的字符串怎么办,其实是很明显的,因为其他字符串只有顺序和逆序2种情况,而且要使得结果最大,肯定选择2种里面较大的那一种,显然是确定的,所以枚举的每一个起始字符以后,可以在O(n)的复杂度内,计算出这个串,然后不断的更新结果,就可以了。
class Solution {
public:
string splitLoopedString(vector<string>& v) {
int n = v.size();
if(n == ) return "";
string res;
for (string&t : v) {
string td = t;
reverse(td.begin(), td.end());
if(td > t)
t = td;
res += t;
}
//cout << res << endl;
for (int i = ; i < n; i++) {
int sz = v[i].size();
for (int j = ; j < sz; j++) {
string ml = v[i].substr(j, sz - j), mr = v[i].substr(, j);
string rl = v[i].substr(j + , sz - j - ), rr = v[i].substr(, j + );
reverse(rl.begin(), rl.end());
reverse(rr.begin(), rr.end());
// ml mr
// rr rl
for (int j = (i + ) % n; j != i; j = (j + ) % n) {
ml += v[j];
rr += v[j];
} ml += mr;
rr += rl;
//cout << ml << endl;
//cout << rr << endl;
res = max(res, max(ml, rr));
}
}
return res;
}
};
LeetCode Weekly Contest 28的更多相关文章
- LeetCode Weekly Contest 8
LeetCode Weekly Contest 8 415. Add Strings User Accepted: 765 User Tried: 822 Total Accepted: 789 To ...
- leetcode weekly contest 43
leetcode weekly contest 43 leetcode649. Dota2 Senate leetcode649.Dota2 Senate 思路: 模拟规则round by round ...
- LeetCode Weekly Contest 23
LeetCode Weekly Contest 23 1. Reverse String II Given a string and an integer k, you need to reverse ...
- Leetcode Weekly Contest 86
Weekly Contest 86 A:840. 矩阵中的幻方 3 x 3 的幻方是一个填充有从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等. 给定一个 ...
- LeetCode Weekly Contest
链接:https://leetcode.com/contest/leetcode-weekly-contest-33/ A.Longest Harmonious Subsequence 思路:hash ...
- 【LeetCode Weekly Contest 26 Q4】Split Array with Equal Sum
[题目链接]:https://leetcode.com/contest/leetcode-weekly-contest-26/problems/split-array-with-equal-sum/ ...
- 【LeetCode Weekly Contest 26 Q3】Friend Circles
[题目链接]:https://leetcode.com/contest/leetcode-weekly-contest-26/problems/friend-circles/ [题意] 告诉你任意两个 ...
- 【LeetCode Weekly Contest 26 Q2】Longest Uncommon Subsequence II
[题目链接]:https://leetcode.com/contest/leetcode-weekly-contest-26/problems/longest-uncommon-subsequence ...
- 【LeetCode Weekly Contest 26 Q1】Longest Uncommon Subsequence I
[题目链接]:https://leetcode.com/contest/leetcode-weekly-contest-26/problems/longest-uncommon-subsequence ...
随机推荐
- 原生js实现瀑布流效果
参考此篇:https://segmentfault.com/a/1190000012621936 以下为个人测试中: css: .masonry{ width:100%; } .item{ posit ...
- Java中“==”、“compareTo()”和“equals()”的区别
在比较两个对象或者数据大小的时候,经常会用到==.compareTo()和equals(),尤其是在接入了Comparable接口后重写compareTo方法等场景,所以我们来理一下这三个的区别. 1 ...
- JavaScript:一句代码输出重复字符串(字符串乘法)
看到一个题目要求写一个函数times,输出str重复num次的字符串. 比如str:bac num:3 输出:abcabcabc 除了利用循环还有几种方法,我学习研究之后记下以下三种方法. 1 ...
- 洛谷——P1196 [NOI2002]银河英雄传说
P1196 [NOI2002]银河英雄传说 题目大意: 给你一个序列,支持两种操作: 合并指令为$M_{i,j}$j,含义为第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所 ...
- python数字取反~
>>> a = [1,2,3,4,5,7,6,4,2,10] >>> h = len(a)//2 >>> h 5 >>> ~h ...
- 第十三节:pandas之groupby()分组
1.Series()对象分组 1.1.单级索引 1.2.多级索引 2.DataFrame()对象分组 3.获取一个分组,遍历分组,filter过滤.
- linux学习9-进程管理知识
Linux 进程管理 实验环境: 用户名:shiyanlou 密码:AJW3tui5 Linux进程之管理控制 实验介绍 通过本实验我们将掌握一些 Linux 所提供的工具来进行进程的查看与控制,掌握 ...
- noip模拟赛 c
分析:一道比较难的爆搜题.首先要把9个块的信息存下来,记录每个块上下左右位置的颜色,然后记录每一排每一列能否操作,之后就是bfs了.在bfs的时候用一个数记录状态,第i位表示原来的第i个块现在在哪个位 ...
- [转]数据库查询 sysobjects
sysobjects sysobjects是系统自建的表,里面存储了在数据库内创建的每个对象(约束.默认值.日志.规则.存储过程等),各在表中占一行.只有在 tempdb 内,每个临时对象才在该表中占 ...
- 跨平台C++开源码的两种经常使用编译方式
作者:朱金灿 来源:http://blog.csdn.net/clever101 跨平台C++开源代码为适应各种编译器的编译,採用了两种方式方面来适配.一种是makefile方式.以著名的空间数据格式 ...