5519. 重新排列单词间的空格 #字符串 #模拟

题目链接

题意

给定字符串text,该字符串由若干被空格包围的单词组成,也就说两个单词之间至少存在一个空格。现要你重新排列空格,使每对相邻单词间空格数目都相等,并尽可能最大化该数目。若不能重新平均分配所有空格,请将多余的空格放置在字符串末尾,这也意味着返回的字符串应当与原text字符串的长度相等。

class Solution {
public:
string reorderSpaces(string text) {
int spacenum = 0, words = 0;
vector<int> pos;
for (int i = 0; i < text.size(); i++){
if(text[i] != ' '){
pos.push_back(i);
while(i < text.size() && text[i] != ' ') i++;
words++;
}
if(text[i] == ' ') spacenum++;
}
if(spacenum == 0) return text;
int last = (words <= 1) ? spacenum : (spacenum % (words - 1));
int gap = (words <= 1) ? 0 : (spacenum / (words - 1));
string ans;
for (int i = 0; i < pos.size(); i ++){
int j = pos[i];
while(j < text.size() && text[j] != ' ') ans += text[j++];
if(i != pos.size() - 1) ans += string(gap, ' ');
}
ans += string(last, ' ');
return ans;
}
};

5520. 拆分字符串使唯一子字符串的数目最大 #深搜 #哈希表

题目链接

题意

给定字符串s,请拆分该字符串,使得拆分出来的若干非空子字符串连接后能够还原为原字符串,返回拆分后唯一子字符串的最大数目。

分析

起初我采用的是贪心策略qaq,将当前未访问过的子串加入map,如果访问过就将该子串连接上后面的字符。然而"addbsd"串得到的答案应是5,也就说拆分成"a","dd","b","s","d",贪心的话会拆分成"a","d","db","s"

观察数据范围,串s的长度最多才16,暴搜可行,递归起点来进行拆分,用map判断该子串是否已出现过。

class Solution {
private:
unordered_map<string, int> vis;
int ans = -1;
public:
void DFS(string s, int start, int cnt){
if(start == s.size())
ans = max(ans, cnt);
else if(cnt + (int)s.size() - start + 1 < ans) //注意s.size()一定要强制转换下
return; //剪枝,此情况说明,无论如何划分都无法超过ans值
else {
string tmp;
for (int i = start; i < s.size(); i++){
tmp += s[i];
if(vis[tmp] == 0){
vis[tmp]++;
DFS(s, i + 1, cnt + 1);
vis[tmp]--;
}
}
}
}
int maxUniqueSplit(string s) {
DFS(s, 0, 0);
return ans;
}
};

5521. 矩阵的最大非负积 #深搜 #动态规划

题目链接

题意

给定rows*cols的矩阵,矩阵每个元素有一整数(可正可负),你从(0,0)出发,只能向右或向下移动。从(0,0)到达(rows-1, cols-1)的所有路径中,找出具有最大非负积的路径,路径之积为沿路径访问的单元格中所有整数的乘积。对最后返回的最大非负积取余,若该结果为负值,则返回-1

分析

  • DFS版本(\(1856ms\)奇慢)
typedef long long ll;
class Solution {
private:
ll ans = -1;
const int MOD = 1e9 + 7;
public:
void DFS(vector<vector<int>>& grid, int x, int y, ll sum){
if((x == grid.size() - 1 && y == grid[x].size() - 1) || sum == 0) {
ans = max(ans, sum); //注意sum==0剪枝,否则超时
return;
}
if(x != grid.size() - 1)
DFS(grid, x + 1, y, (sum * (ll)grid[x + 1][y]));
if(y != grid[x].size() - 1)
DFS(grid, x, y + 1, (sum * (ll)grid[x][y + 1]));
}
int maxProductPath(vector<vector<int>>& grid) {
DFS(grid, 0, 0, (ll)grid[0][0]);
return ans % MOD; //由于涉及到值间比较,只对最后结果取模即可
}
};
  • DP版本(\(8ms\))

我们定义一个三维数组DP[i][j][0\1],代表从(0,0)到达(i,j)的最小乘积和最大乘积。为什么要维护两个最值,考虑到乘积的过程中会遇到负数,最大之积乘上负数便成为最小之积了。此外,未到达终点之前移动过程中得到的负数,在后面有机会被另一负数抵消为正数,因而需要维护这两个最值。注意,遇到负数时,即变号的过程,要切换两个最值。

typedef long long ll;
class Solution {
private:
const int MOD = 1e9 + 7;
ll DP[16][16][2];
public:
int maxProductPath(vector<vector<int>>& grid) {
DP[0][0][1] = DP[0][0][0] = grid[0][0];
int n = grid.size(), m = grid[0].size();
for(int i = 1; i < n; i++) //初始化边界,只有一个方向无需选择
DP[i][0][0] = DP[i][0][1] = DP[i - 1][0][1] * (ll)grid[i][0];
for(int j = 1; j < m; j++) //初始化边界,只有一个方向无需选择
DP[0][j][0] = DP[0][j][1] = DP[0][j - 1][1] * (ll)grid[0][j];
for(int i = 1; i < n; i++){
for(int j = 1; j < m; j++){
if(grid[i][j] == 0) continue;
else if(grid[i][j] > 0){ //不用变号
DP[i][j][1] = max(DP[i][j - 1][1] * (ll)grid[i][j], DP[i - 1][j][1] * (ll)grid[i][j]);
DP[i][j][0] = min(DP[i][j - 1][0] * (ll)grid[i][j], DP[i - 1][j][0] * (ll)grid[i][j]);
}
else if(grid[i][j] < 0){ //乘上grid[i][j]会使结果变号
DP[i][j][1] = max(DP[i][j - 1][0] * (ll)grid[i][j], DP[i - 1][j][0] * (ll)grid[i][j]); //最大值成负值变为最小值
DP[i][j][0] = min(DP[i][j - 1][1] * (ll)grid[i][j], DP[i - 1][j][1] * (ll)grid[i][j]);
}
}
}
if(DP[n - 1][m - 1][1] < 0) return -1;
else return DP[n - 1][m - 1][1] % MOD;
}
};

1595. 连通两组点的最小成本 #状压DP #枚举子集 #滚动数组

题目链接

题意

给定两组点,第一组有size1个点,第二组有size2个点,size1>=size2。给定cost[][]数组,cost[i][j]代表第一组点i和第二组点j的连接成本。现要你将第一组的每个点都必须至少与第二组中一个点连接,且第二组的每个点都必须至少与第一组中的一个点连接,返回连通两组点所需的最小成本。

分析

这道题,让我意识到我状压DP练习得还不够qaq。

由于第一组点的数量大于第二组点,也就说状态比第二组点多,那么定义dp[i][state]代表当前遍历到左侧的第i个节点时,与第二组顶点的连接状态state(二进制意义,连接为1,未连接为0)下,所需的最小成本

对于左侧第i个节点,它有两种连接方案:

  1. 选择右侧中已被连接的一个点,进行连接(保证所有的左侧点均与右侧点相连)。也许你会觉得多余,但是你看上图中的顶点3,它只能与A匹配(之前被顶点1连接过)。

  2. 选择右侧中未被连接的若干个点,进行连接。(保证所有右侧点均被左侧点相连)如上图中的顶点2。此处要连接的数量,一时无法确定下来,故需要枚举,也就说枚举当前未连接点状态的子集,我们不妨将这状态反转(未连接的表示1,与上面不同,这样是便于与上一状态进行合并,按位或操作取并集),比如当前状态00111说明,右侧3,4,5点未被左侧点连接,那么该状态子集为00001,00010,00011,….。

    [位运算枚举子集的方法]:参考自@lucifer1004的笔记

    模板即为

    for(int state = 0; state < mymax; state++){ //枚举所有状态
    int rest = ....
    for(int subState = res; subState; subState = (subState - 1) & res){
    //....
    } //从后往前,枚举子集:即不断-1的过程中,对状态-1得到的结果与最初母集进行按位和操作,比如"1100",1100-1=1011,我们看到后面两位的11并不是"1100"的子集,那么将1011按位与1100得到1000,属于1100的子集
    }

    n个元素所有子集进行枚举,时间复杂度\(O(3^n)\)。

状态压缩DP往往能通过滚动数组节省空间,注意到每次迭代左侧的一个顶点时,当前新状态只依赖于上一状态结果即可(即当前顶点的上一个顶点或上一轮循环计算的结果),因而我们可以对dp[i][state]的第一维滚掉,只需定义dp[state]即可。

class Solution {
private:
int dp[5000];
public:
int connectTwoGroups(vector<vector<int>>& cost) {
int n = cost.size(), m = cost[0].size(), mymax = 1 << m;
for (int i = 0; i <= mymax; i++) dp[i] = 0x3f3f3f3f;
dp[0] = 0;
for (int i = 0; i < n; i++) { //迭代每一行(即左侧的每一个顶点)
int tmp[5000]; for (int i = 0; i <= mymax; i++) tmp[i] = 0x3f3f3f3f;
for (int state = 0; state < mymax; state++) { //枚举 第二组连接的所有状态
if (dp[state] == 0x3f3f3f3f) continue; //状态不可达
/*方案一:对于左侧点,选择右侧已被连接的一个点匹配*/
for (int j = 0; j < m; j++) {
int nextState = state | (1 << j);
tmp[nextState] = min(tmp[nextState], dp[state] + cost[i][j]);
}
/*方案二:对于右侧点,选择state状态所有未被连接的点的一个子集里的点进行连接。*/
int rest = (mymax - 1) ^ state; //表示截至第 i 行还没被选过的边(即未连上第二组相应顶点)
for (int subState = rest; subState; subState = (subState - 1) & rest) { //枚举之前未连接的边的子集
int sum = 0;
for (int j = 0; j < m; j++) //计算该子集中未连接的边所需要的花费
if (subState & (1 << j)) sum += cost[i][j]; //若子集中存在该边,计入
int nextState = state | subState; //之前的边 并上 部分之前未连接的边
tmp[nextState] = min(tmp[nextState], dp[state] + sum); //更新新状态
}
}
for (int i = 0; i <= mymax; i++) dp[i] = tmp[i]; //滚掉上一轮的状态,更新当前轮的状态,节省空间
}
return dp[mymax - 1]; //返回第二组所有顶点均被连上之状态 的 值
}
};

LeetCode周赛#207的更多相关文章

  1. 【Leetcode周赛】从contest-111开始。(一般是10个contest写一篇文章)

    Contest 111 (题号941-944)(2019年1月19日,补充题解,主要是943题) 链接:https://leetcode.com/contest/weekly-contest-111 ...

  2. 拼写单词[哈希表]----leetcode周赛150_1001

    题目描述: 给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars. 假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我 ...

  3. 【Leetcode周赛】从contest-41开始。(一般是10个contest写一篇文章)

    Contest 41 ()(题号) Contest 42 ()(题号) Contest 43 ()(题号) Contest 44 (2018年12月6日,周四上午)(题号653—656) 链接:htt ...

  4. 【Leetcode周赛】从contest-51开始。(一般是10个contest写一篇文章)

    Contest 51 (2018年11月22日,周四早上)(题号681-684) 链接:https://leetcode.com/contest/leetcode-weekly-contest-51 ...

  5. 【Leetcode周赛】从contest-71开始。(一般是10个contest写一篇文章)

    Contest 71 () Contest 72 () Contest 73 (2019年1月30日模拟) 链接:https://leetcode.com/contest/weekly-contest ...

  6. 【Leetcode周赛】从contest-81开始。(一般是10个contest写一篇文章)

    Contest 81 (2018年11月8日,周四,凌晨) 链接:https://leetcode.com/contest/weekly-contest-81 比赛情况记录:结果:3/4, ranki ...

  7. 【Leetcode周赛】从contest-91开始。(一般是10个contest写一篇文章)

    Contest 91 (2018年10月24日,周三) 链接:https://leetcode.com/contest/weekly-contest-91/ 模拟比赛情况记录:第一题柠檬摊的那题6分钟 ...

  8. 【Leetcode周赛】从contest-121开始。(一般是10个contest写一篇文章)

    Contest 121 (题号981-984)(2019年1月27日) 链接:https://leetcode.com/contest/weekly-contest-121 总结:2019年2月22日 ...

  9. 【Leetcode周赛】从contest1开始。(一般是10个contest写一篇文章)

    注意,以前的比赛我是自己开了 virtual contest.这个阶段的目标是加快手速,思考问题的能力和 bug-free 的能力. 前面已经有了100个contest.计划是每周做三个到五个cont ...

随机推荐

  1. Redis---07主从复制(哨兵模式)

    一.什么是哨兵模式 基于主从复制的一般模式(一主二从)下,当发生主机发生宕机时,会通过流言协议判断主机是不是宕机,是的话则会通过投票协议自动把某一个从机转换成主机. 二.设置哨兵模式的配置文件 通过r ...

  2. Java数据结构-01顺序表

    一.定义 线性表是一种线性结构,它是具有相同类型的n(n≥0)个数据元素组成的有限序列. 二.存储分类 1.顺序存储: ①简述:是指将线性表中的各个元素依次存放在一组地址连续的存储单元中,通常将这种方 ...

  3. LoadRunner11web压力测试录制、回放、负载前的准备

    以前都是利用LoadRunner进行接口测试.自动化测试.压力测试.最近要对web系统做录制压测,因此花费了很长时间来研究这方面的工作.以下是我web端录制.压测过程的流程以及遇到的坑. 一.启动Vi ...

  4. day80:luffy:短信sdk接入&点击获取验证码&注册功能的实现&Celery实现短信发送功能

    目录 1.短信sdk接入 2.前端点击获取验证码效果 3.注册后端接口实现 4.注册-前端 5.Celery 6.Celery完成短信发送功能 1.短信sdk接入 1.准备工作 1.下载云通讯相关的文 ...

  5. 正式班D21

    2020.11.03星期二 正式班D21 目录 11.5 源码包 11.5.1 预先安装编译安装依赖的库 11.5.2 官网下载源码包 11.5.3 解压.编译.编译安装 11.5 源码包 11.5. ...

  6. 打印Sql查询语句

    如果在使用了yii的查询语句的话,可以打印本次的Sql,可以用 $model->find()->createCommand()->getRawSql();此语句返回的就是sql查询语 ...

  7. Java集合(类)框架(一)

    集合类均在java.util包之下 集合类方法的功能基本为增.删.改.查,部分外加方法除外(如toArray().toString()等) 1.List接口 底层为Object 数组,存放的数据可以重 ...

  8. C/C++模运算(正负整数)

    模运算 模运算:又称为取余运算 正整数的模运算 对于正整数a,b 如果\(a=q\times b+r\)其中\(0\le r < b\) 则有\(a \bmod b=r\)即 \(a\%b=r\ ...

  9. js 图片放大镜功能

    原理:放置两张相同的图片,一张作为主图片(图片1),另一张作为用来裁剪并放大的图片(图片2)          鼠标移动时,计算鼠标在图片1的位置(距离图片1左上角的x,y距离),以此决定在图片2开始 ...

  10. 卡特兰数 洛谷P1641 [SCOI2010]生成字符串

    卡特兰数 参考博客 介绍 卡特兰数为组合数学中的一种特殊数列,用于解决一类特殊问题 设\(f(n)\)为卡特兰数的第n项 其通项公式为 \[f(n)=\frac{2n\choose n}{n+1} \ ...