5.14 note
关于在DFS当中不要随便用return这件事情
关于在DFS当中不要随便用return这件事情,
虽然引入了valid进行标记,
但是这个区域块儿还是要遍历完的。
如果发现不存在就continue结束。
这次判断,但是整个联通块还是要找到的,如果草率的采用return的话就会导致输出的结果偏大,因为有的联通的地方都没有处理完。
class Solution {
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int ret = 0, m, n;
vector<vector<bool>> check;
public:
int largestArea(vector<string>& grid)
{
m = grid.size();
n = grid[0].size();
check.resize(m, vector<bool>(n, false));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (!check[i][j] && grid[i][j] != '0') {
int cur = 1;
bool valid = true; // 有效性标记
check[i][j] = true;
dfs(grid, i, j, cur, valid); // 增加valid参数
if (!valid) cur=0;
ret = max(ret, cur);
}
}
}
return ret;
}
void dfs(vector<string>& grid, int a, int b, int& count, bool& valid)
{
for (int k = 0; k < 4; k++) {
int x = a + dx[k], y = b + dy[k];
// 检查是否越界
if (x < 0 || y < 0 || x >= m || y >= n) {
valid = false;
continue; //不能用return
}
// 检查是否是走廊
if (grid[x][y] == '0') {
valid = false;
continue;
}
if (!check[x][y] && grid[x][y] == grid[a][b]) {
check[x][y] = true;
count++;
dfs(grid, x, y, count, valid);
}
}
}
};
二维dp
class Solution {
public:
int countVowelStrings(int n)
{
vector<char> cmp=
{'a','e','i','o','u'};
vector<vector<int>>
dp(n+1,vector<int>(5,0));
//i长 j结尾的字符串 个数
for(int i=0;i<5;i++)
dp[1][i]=1;
for(int i=2;i<=n;i++)
{
for(int j=0;j<5;j++)
{
for(int k=0;k<=j;k++)
{
dp[i][j]+=dp[i-1][k];
}
}
}
int ret=0;
for(int i=0;i<5;i++)
ret+=dp[n][i];
return ret;
}
};
背包问题
i个物品,j个质量
01背包
二维数组 到 一维数组
因为01 背包要用到上一行,所以优化的时候 要逆序,防止覆盖
- 限制 装满
题目
二维背包
class Solution {
public:
// 前i 个 和为j 的子序列长度
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> dp(n + 1, vector<int> (target + 1, -0x3f3f3f3f));
dp[0][0] = 0;
for(int i = 0; i < n; ++ i){
for(int j = 0; j <= target; ++ j){
dp[i + 1][j] = dp[i][j];
if(j - nums[i] >= 0){
dp[i + 1][j] = max(dp[i + 1][j], dp[i][j - nums[i]] + 1);
}
}
}
if(dp[n][target] > 0)
return dp[n][target];
return -1;
}
};
一维优化
class Solution {
public:
// 前i 个 和为j 的子序列长度
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<int> dp(target + 1, -0x3f3f3f3f);
dp[0] = 0;
for(int i = 0; i < n; ++ i){
for(int j = target; j>=0; --j){
//逆序哦
dp[j] = dp[j];
if(j - nums[i] >= 0){
dp[j] = max(dp[j], dp[j - nums[i]] + 1);
}
}
}
if(dp[target] > 0)
return dp[target];
return -1;
}
};
完全背包: 一个可以选多
优化时 不用逆序
限制装满
多线程 & 线程池
关于加锁与线程线性化
- 加锁会使部分代码路径线性化:当多个线程访问共享资源时,加锁确实会让竞争该锁的线程在同一时刻只有一个能进入临界区访问资源,从这个角度看,在临界区内的代码执行是线性的。
- 多线程仍有用处:多线程的作用不仅仅在于对共享资源的并发访问。即使有锁存在,线程在其他未加锁的代码区域仍可以并发执行,例如不同线程可以同时处理不同的任务队列,或者在等待锁的过程中执行其他非阻塞的操作。而且,多线程可以提高程序的响应性,比如在图形界面应用中,一个线程用于处理用户界面交互,其他线程进行数据处理等,防止界面卡顿。
关于多个线程池调用CPU
- 线程池与CPU的关系:线程池中的线程会由操作系统调度到CPU核心上执行。多个线程池中的线程会竞争CPU资源。
- 操作系统的调度:操作系统会根据线程的优先级、任务的紧急程度等因素,采用不同的调度算法来分配CPU时间片给各个线程。例如,常见的调度算法有时间片轮转调度算法,它会给每个线程分配一定时间的时间片,当时间片用完后,即使线程没有执行完,也会被暂停,然后操作系统会选择其他线程执行,以此来实现多个线程对CPU的分时复用,从而在宏观上实现多个线程池中的线程并发执行。
总之,加锁虽然会使临界区代码线性执行,但不影响多线程在其他方面发挥作用;而多个线程池通过操作系统的调度来合理利用CPU资源,实现任务的并发处理。