每日一题之地宫取宝

题目描述

X 国王有一个地宫宝库。是 n×m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是 k 件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 k 件宝贝。

输入描述

输入一行 3 个整数,用空格分开:n,m,k (1≤n,m≤50,1≤k≤12)。

接下来有 n 行数据,每行有 m 个整数 Ci (0≤Ci≤12) 代表这个格子上的宝物的价值。

输出描述

要求输出一个整数,表示正好取 k 个宝贝的行动方案数。该数字可能很大,输出它对 109+7 取模的结果。

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;// 全局常量定义
const int MOD = 1e9 + 7;          // 模数取10^9+7
const int MAX_K = 13;             // 最大可取宝物数+1(k≤12)
const int MAX_VAL = 14;           // 价值上限(原价值0-12,+1后1-13)
int n, m, k;                      // 地宫行列数和目标宝物数
vector<vector<int>> grid;          // 地宫宝物价值矩阵// 四维记忆数组:memo[i][j][cnt][max_val] 表示在(i,j)位置,
// 已取cnt个宝物且当前最大价值为max_val的状态方案数[3](@ref)
int memo[50][50][13][14];         // 按题目最大范围50x50x12x14// 深度优先搜索函数
// 参数:当前位置(i,j),已收集宝物数cnt,当前最大价值max_val
// 返回:从当前位置到终点的合法路径数[8](@ref)
int dfs(int i, int j, int cnt, int max_val) {// 终点判断(右下角格子)if (i == n-1 && j == m-1) {int cases = 0;// 情况1:已收集k件且不取终点[6](@ref)if (cnt == k) cases++; // 情况2:已收集k-1件且可取终点宝物if (cnt == k-1 && grid[i][j] > max_val) cases++;return cases % MOD;}// 记忆化查询(已计算过的状态直接返回)if (memo[i][j][cnt][max_val] != -1) return memo[i][j][cnt][max_val];int res = 0;// 不取当前格子的两种情况(向右/向下移动)if (j+1 < m)  // 向右移动合法时res = (res + dfs(i, j+1, cnt, max_val)) % MOD;if (i+1 < n)  // 向下移动合法时res = (res + dfs(i+1, j, cnt, max_val)) % MOD;// 尝试取当前格子的两种情况(需满足价值递增条件)if (grid[i][j] > max_val && cnt < k) {int new_cnt = cnt + 1;int new_max = grid[i][j];// 更新状态后继续递归if (j+1 < m) res = (res + dfs(i, j+1, new_cnt, new_max)) % MOD;if (i+1 < n)res = (res + dfs(i+1, j, new_cnt, new_max)) % MOD;}// 记录当前状态并返回结果return memo[i][j][cnt][max_val] = res;
}int main() {// 输入处理cin >> n >> m >> k;grid.resize(n, vector<int>(m));for (int i = 0; i < n; ++i)for (int j = 0; j < m; ++j) {cin >> grid[i][j];grid[i][j]++;  // 价值+1处理(避免0值冲突)[3](@ref)}// 初始化记忆数组为-1(表示未计算状态)memset(memo, -1, sizeof(memo));// 从起点(0,0)开始,初始状态未取任何宝物(cnt=0,max_val=0)cout << dfs(0, 0, 0, 0) << endl;return 0;
}

DFS的基本思路应该是:从起点(0,0)开始,每次选择向右或向下移动,同时记录当前已经收集的宝物数量以及当前的最大价值。当到达终点时,如果收集的宝物数量正好是k,则计数加一。

不过,这样的纯递归会有很多重复计算,导致效率极低。因此,通常会使用记忆化搜索(即DFS+备忘录)来优化。记忆化搜索需要记录状态,包括当前位置(i,j)、已收集的数量cnt、当前最大价值max_val。这样可以将已经计算过的状态保存下来,避免重复计算。

在动态规划中,状态是四维的:位置(i,j)、数量c、最大价值v。同样,在记忆化DFS中,也需要同样的状态参数。因此,记忆化数组应该是四维的,或者至少包含这四个关键状态。

另外,需要注意的是,宝物的价值可能为0,但为了处理初始状态(没有拿任何宝物时,max_val可以设为-1或者0,但需要统一处理)。在之前的动态规划解法中,宝物价值被加1,以避免0的问题,可能在这里也需要类似的处理。

所以,DFS的步骤大概是这样的:

  1. 参数:当前位置(i,j),已拿的数量cnt,当前最大价值max_val。
  2. 终止条件:到达终点(n-1,m-1),如果此时cnt等于k,返回1,否则返回0。
  3. 递归转移:向右和向下移动。
  4. 在每个位置,有两种选择:拿当前宝物或不拿。
    a. 如果拿,需要满足当前宝物的价值大于max_val,并且cnt < k。
    b. 不拿的话,直接转移到下一个位置。
  5. 使用记忆化数组来存储已经计算过的状态,避免重复计算。

但是,直接这样实现可能会有问题,比如当max_val的值范围较大时,数组会很大。不过题目中宝物的价值最多是12,加上1之后是13,所以max_val的可能取值是0到13(初始状态设为-1,但处理后变为0)。

另外,需要处理数组越界的问题,即i和j不能超过n-1和m-1。

现在,我需要考虑如何初始化记忆化数组。由于n和m最多是50,k是12,max_val是0到13,所以记忆化数组的维度是50x50x13x14,这在C++中应该是可行的,但需要注意内存使用。不过,题目中的结果需要对1e9+7取模,所以用int或long long存储。

接下来,编写代码的大致结构:

  • 定义dfs函数,参数是i,j,cnt,max_val。
  • 如果到达终点,检查cnt是否等于k。
  • 对于当前位置的宝物,判断是否拿取。
  • 递归向右和向下移动,并将结果相加。
  • 使用记忆化数组保存结果,避免重复计算。

另外,处理拿取宝物的情况时,如果当前宝物的价值大于max_val,并且cnt+1 <=k,则可以拿取,此时新的max_val变为当前宝物的价值,cnt+1。

需要注意的是,初始状态时,max_val可能是-1(如果还没有拿任何宝物),或者根据处理后的值进行调整。例如,在动态规划中,宝物价值被加1,所以初始max_val可以是0,这样当第一次拿的宝物价值为0(处理后为1)时,可以正确比较。

因此,在DFS中,宝物价值也应该加1,这样初始的max_val可以设为0(表示没有拿任何宝物),当第一次拿的时候,只要当前宝物的价值(已加1)>0即可。

所以,在代码中,输入的宝物价值需要先加1处理,类似于动态规划的做法。

现在,综合这些思路,可以开始编写代码框架:

定义memo[i][j][cnt][max_val],表示在(i,j)位置,已经拿了cnt个宝物,当前最大价值是max_val时的路径数。

递归函数的大致逻辑:

int dfs(int i, int j, int cnt, int max_val) {
if (i == n-1 && j == m-1) {
return cnt == k ? 1 : 0;
}
if (memo[i][j][cnt][max_val] != -1) {
return memo[i][j][cnt][max_val];
}
int res = 0;
// 向右走
if (j+1 < m) {
// 不拿下一个位置的宝物
res += dfs(i, j+1, cnt, max_val);
res %= MOD;
// 拿下一个位置的宝物,前提是满足条件
int next_val = grid[i][j+1];
if (cnt < k && next_val > max_val) {
res += dfs(i, j+1, cnt+1, next_val);
res %= MOD;
}
}
// 向下走
if (i+1 < n) {
// 不拿
res += dfs(i+1, j, cnt, max_val);
res %= MOD;
// 拿
int next_val = grid[i+1][j];
if (cnt < k && next_val > max_val) {
res += dfs(i+1, j, cnt+1, next_val);
res %= MOD;
}
}
memo[i][j][cnt][max_val] = res;
return res;
}

不过,这样的写法可能有问题,因为当移动到下一个位置时,处理的是下一个位置的宝物。例如,当向右走到(i,j+1)时,是否拿的是grid[i][j+1]的宝物。所以,在当前位置处理的是当前位置的宝物,或者下一个位置的?

这里可能存在逻辑错误。正确的做法应该是:当移动到下一个位置(比如向右走到(i,j+1)),此时处理的是该位置的宝物。因此,在到达(i,j)时,是否拿取的是grid[i][j]的宝物?

或者,可能在递归时处理的是当前所在位置的宝物。例如,小明在移动到(i,j)时,可以选择是否拿取这里的宝物,然后继续移动。

所以,可能需要重新调整递归的逻辑:当小明到达(i,j)时,处理该位置的宝物,然后移动到右边或下边。

这样,初始调用应该是从起点(0,0)开始,处理是否拿取起点处的宝物,然后递归到右边或下边。

因此,递归函数的结构可能需要调整:

在到达(i,j)时,有两种情况:已经拿了这里的宝物,或者没有拿。因此,在递归时,需要考虑在当前位置是否拿取宝物,然后处理移动。

这可能更符合问题的逻辑。例如,小明走到(i,j)时,可以选择是否拿这里的宝物(如果满足条件),然后继续向右或向下走。

因此,递归函数的结构可能需要重新设计:

函数参数包括当前位置(i,j),已经拿的数量cnt,当前最大价值max_val。当处理当前位置时,可以选择拿或者不拿,然后移动。

例如,在函数内部:

  1. 处理当前位置是否拿取宝物。
    a. 如果之前没有拿过这里的宝物,现在可以选择拿,前提是当前宝物的价值大于max_val,并且cnt <k。
    b. 或者选择不拿,不管之前是否拿过。

但这样的话,可能需要记录是否已经在该位置拿过宝物,但题目中每个格子只能经过一次,所以每个格子只能处理一次是否拿取,不能在之后返回时再拿。

因此,正确的处理流程是:当小明第一次到达(i,j)时,决定是否拿取该宝物,然后继续移动,不能再回来拿。

因此,递归函数的结构应该是这样的:

当到达(i,j)时,首先处理是否拿取该位置的宝物,然后向右或向下移动。

因此,递归函数的参数应该包括当前位置(i,j),已拿数量cnt,当前最大价值max_val。在到达该位置时,处理拿或者不拿的选择,然后递归到下一个位置。

这样,初始调用应该是在起点(0,0),此时可以选择拿或者不拿起点的宝物,然后递归到右边或下边。

因此,递归函数的逻辑应该调整为:

在(i,j)位置时:

  • 如果不拿这里的宝物,那么保持cnt和max_val不变,向右或向下移动。
  • 如果拿这里的宝物,前提是grid[i][j] > max_val,并且cnt+1 <=k,那么更新cnt和max_val,然后移动。

这样,递归的处理应该是在每个位置处理拿或不拿的选择,然后再移动。而不是在移动之后处理下一个位置的宝物。

这可能更正确,因为小明在走到某个格子时,才决定是否拿该格子的宝物,而不是在移动前处理下一个位置的宝物。

所以,正确的递归步骤应该是:

到达(i,j)时:

  1. 先处理是否拿这个格子的宝物:
    a. 不拿:此时状态保持不变,继续向右或向下移动。
    b. 拿:如果当前宝物的价值大于max_val,并且cnt <k,那么cnt+1,max_val更新为当前宝物的价值,然后移动。
  2. 移动到右边或下边,前提是不越界。

同时,需要考虑到达终点时的处理:必须在终点处完成所有选择,即是否拿终点的宝物,使得最终数量正好是k。

因此,递归函数的终止条件是当i ==n-1且j ==m-1时,处理完该位置的拿或不拿后,检查cnt是否等于k。

这样,整个递归的结构需要调整。

例如:

int dfs(int i, int j, int cnt, int max_val) {
// 处理当前格子是否拿取
if (i == n-1 && j == m-1) {
// 到达终点,检查当前处理后的cnt是否等于k
// 这里需要处理是否拿终点处的宝物
int ans =0;
// 不拿的情况
if (cnt ==k) {
ans +=1;
}
// 拿的情况,如果满足条件
if (cnt <k && grid[i][j] > max_val && cnt+1 ==k) {
ans +=1;
}
return ans % MOD;
}
if (memo[i][j][cnt][max_val] != -1) {
return memo[i][j][cnt][max_val];
}
int res =0;
// 处理当前格子是否拿取
// 选择不拿
// 然后移动
if (j+1 <m) { // 向右
res += dfs(i, j+1, cnt, max_val);
res %= MOD;
}
if (i+1 <n) { // 向下
res += dfs(i, j+1, cnt, max_val); // 应该是i+1, j?
res %= MOD;
}
// 这里可能写错了,应该是i+1,j?
// 修正:
if (i+1 <n) {
res += dfs(i+1, j, cnt, max_val);
res %= MOD;
}

// 选择拿当前格子宝物
if (grid[i][j] > max_val && cnt <k) {int new_cnt = cnt +1;int new_max = grid[i][j];// 移动if (j+1 <m) {res += dfs(i, j+1, new_cnt, new_max);res %= MOD;}if (i+1 <n) {res += dfs(i+1, j, new_cnt, new_max);res %= MOD;}
}memo[i][j][cnt][max_val] = res;
return res;

但这样处理的话,在到达每个位置时,会有两种选择:拿或者不拿当前宝物,然后继续移动。这样,在递归中,每个位置的处理分为两种情况,导致状态数翻倍。但根据题目要求,小明在走到某个格子时,可以选择拿或者不拿,但一旦离开该格子就不能再回来拿。

因此,这样的结构是正确的。而初始调用应该是从起点开始,处理拿或不拿起点的宝物,然后递归。

但此时,初始调用应该如何?例如,小明在起点(0,0)处,可以选择拿或不拿:

所以,最终的答案应该是:

dfs(0,0,0,0) // 不拿起点宝物的情况,加上 dfs(0,0,1, grid[0][0]) // 拿起点宝物的情况?

或者,在dfs函数内部处理是否拿取当前宝物?

这需要重新设计递归函数,使其处理的是在当前位置的拿或不拿的选择,而不是在移动之后处理下一个位置的选择。

这可能更合理。例如,在函数中,当处理到(i,j)时,首先处理是否拿取这里的宝物,然后递归到右边和下面的格子。

此时,递归函数的参数是当前位置(i,j),已经拿的数量cnt,当前最大价值max_val,表示的是到达该位置时已经做的选择(是否拿取该位置的宝物之前的状态)。

例如,当小明到达(i,j)时,还没有决定是否拿这里的宝物,所以函数内部处理是否拿取,然后移动。

或者,参数表示的是已经处理完该位置的拿取状态,即到达(i,j)时,已经完成了拿或不拿的选择,此时的状态是cnt和max_val。然后,递归到右边和下面的格子。

这可能需要将递归函数分为两种情况:在处理完当前位置的选择后,移动。

例如,函数dfs(i,j,cnt,max_val)表示的是小明在(i,j)位置,已经处理过是否拿这里的宝物,现在要移动到下一个格子。这可能不太对。

或者,另一种设计方式是:当小明移动到(i,j)时,处理是否拿这里的宝物,然后继续移动。因此,函数参数表示的是到达(i,j)时的状态(比如,还没有拿这里的宝物),然后在函数内部处理拿或者不拿的选择,再移动。

这可能会更复杂,因为每个位置的处理分为两种情况:拿或者不拿。

或者,将函数参数设计为到达(i,j)时的状态,包括是否已经拿取了该位置的宝物。但这会增加状态参数,例如一个布尔标志,表示是否已经处理过该位置的拿取。这会导致状态数进一步增加,可能不可行。

因此,可能需要重新考虑递归函数的设计,使其在到达(i,j)时,处理拿或不拿的选择,然后移动。

正确的做法应该是:小明到达(i,j)格子,此时可以决定是否拿该格子的宝物,前提是满足条件。然后,无论是否拿取,都必须向右或向下移动,直到终点。

因此,递归函数的参数应该是在到达(i,j)时的状态(即已经拿的数目cnt和当前最大max_val),然后处理是否拿取该格子的宝物,然后移动到下一个格子。

例如:

在函数dfs(i,j,cnt,max_val)中:

  • 处理是否拿取当前格子(i,j)的宝物。
    a. 不拿:此时保持cnt和max_val不变,继续向右或向下移动。
    b. 拿:如果当前宝物的价值大于max_val,并且cnt+1 <=k,则更新cnt和max_val,再移动。

需要注意的是,每个格子只能处理一次拿取决策,即小明在第一次到达该格子时决定是否拿取,之后不能返回。

因此,递归函数的正确逻辑应该是:

当小明到达(i,j)时,处理该格子的拿取选择,然后移动。

所以,递归函数的调用应该是在处理完当前格子之后,移动到下一个格子。例如:

int dfs(int i, int j, int cnt, int max_val) {
// 如果已经处理过这个状态,直接返回
if (memo[i][j][cnt][max_val] != -1) {
return memo[i][j][cnt][max_val];
}

// 当前格子是否处理拿取?
// 不拿的情况:此时不改变cnt和max_val,直接移动
// 拿的情况:如果满足条件,更新cnt和max_val,然后移动int res =0;// 先处理不拿当前格子的情况,直接移动
// 但需要移动到右边或下边
// 但此时需要移动到下一个格子,所以在移动之后处理下一个格子的选择?// 不,这样可能不正确,因为当前格子的处理应该是在该格子停留时进行的选择,而不是移动后的格子。// 正确的做法是,在当前位置处理是否拿取,然后移动到下一个位置时处理下一个位置的选择。// 因此,当前函数处理的是到达(i,j)后的选择,然后移动到下一个位置。// 因此,需要先处理是否拿取当前格子,然后递归到下一个位置。// 所以,处理当前格子的拿取选择:// 情况一:不拿当前格子
int new_cnt = cnt;
int new_max = max_val;
// 此时,移动到右边或下边,递归处理下一个格子
if (j < m-1) { // 可以向右res += dfs(i, j+1, new_cnt, new_max);res %= MOD;
}
if (i < n-1) { // 可以向下res += dfs(i+1, j, new_cnt, new_max);res %= MOD;
}// 情况二:拿当前格子,如果满足条件
if (grid[i][j] > new_max && new_cnt <k) {int take_cnt = new_cnt +1;int take_max = grid[i][j];// 移动后处理下一个格子if (j < m-1) {res += dfs(i, j+1, take_cnt, take_max);res %= MOD;}if (i <n-1) {res += dfs(i+1, j, take_cnt, take_max);res %= MOD;}
}// 如果当前是终点,需要处理特殊情况?
if (i ==n-1 && j ==m-1) {// 在终点处,是否已经处理了拿或不拿的情况?// 此时,如果在终点处不拿,且cnt ==k,则res +=1// 如果在终点处拿,并且满足条件,且 cnt+1 ==k,则res +=1// 所以,在终点处,两种情况都要考虑// 但根据上面的处理,当到达终点时,移动操作会越界,所以需要在此时检查是否到达终点// 上面的处理可能无法覆盖终点的情况,所以需要单独处理终点处的情况// 例如,当i,j是终点时,移动的话j+1或i+1会越界,所以此时无法继续移动,递归终止// 所以,当到达终点时,是否拿取该格子的宝物,然后判断是否满足条件int current_res =0;// 不拿的情况:如果已经处理了不拿,此时移动会导致越界,所以在这里处理if (i ==n-1 && j ==m-1) {if (new_cnt ==k) {current_res +=1;}}// 拿的情况:if (grid[i][j] > new_max && new_cnt <k) {if (new_cnt +1 ==k) {current_res +=1;}}res += current_res;res %= MOD;
}memo[i][j][cnt][max_val] = res;
return res;

但这样的处理可能比较复杂,尤其是在终点处的判断。因为当i,j是终点时,j+1和i+1都会越界,所以在移动到下个位置时会直接返回0,而真正的判断需要在处理当前格子时进行。

因此,在递归函数中,当处理到终点(i,j)时,无论是选择拿还是不拿,都需要立即判断是否满足条件,并返回相应的结果,而不是继续移动。

因此,递归函数的终止条件应该是在处理完当前格子(终点)后,检查是否满足k件宝物。

因此,正确的递归终止条件应该是在处理当前位置是否为终点时,无论是拿还是不拿,都要计算可能的情况。

这可能需要在递归函数的开始部分处理:

int dfs(int i, int j, int cnt, int max_val) {
// 检查是否已经缓存
if (memo[i][j][cnt][max_val] != -1) {
return memo[i][j][cnt][max_val];
}

// 处理终点情况
if (i == n-1 && j == m-1) {int ans =0;// 不拿该格子的情况if (cnt ==k) {ans +=1;}// 拿该格子的情况if (cnt <k && grid[i][j] > max_val && cnt+1 ==k) {ans +=1;}ans %= MOD;memo[i][j][cnt][max_val] = ans;return ans;
}// 非终点情况,处理拿或不拿的选择
int res =0;// 不拿当前格子,直接移动
if (j+1 <m) {res += dfs(i, j+1, cnt, max_val);res %= MOD;
}
if (i+1 <n) {res += dfs(i+1, j, cnt, max_val);res %= MOD;
}// 拿当前格子,如果满足条件
if (grid[i][j] > max_val && cnt <k) {int new_cnt = cnt+1;int new_max = grid[i][j];if (j+1 <m) {res += dfs(i, j+1, new_cnt, new_max);res %= MOD;}if (i+1 <n) {res += dfs(i+1, j, new_cnt, new_max);res %= MOD;}
}memo[i][j][cnt][max_val] = res;
return res;

这样,当到达终点时,处理是否拿取该位置的宝物,并判断是否满足cnt==k或者cnt+1 ==k(如果是拿的情况)。

这样,递归函数的初始调用应该是:

int ans = dfs(0, 0, 0, 0);

但是,这样的话,在起点处,小明可以选择不拿或者拿。但根据上述代码,在起点处的处理会被自动处理吗?

是的,因为在递归函数中,当调用dfs(0,0,0,0)时,会处理是否拿取(0,0)的宝物。例如,在非终点的情况下,处理不拿和拿的情况:

  • 不拿的话,移动时cnt还是0,max_val是0。
  • 拿的话,需要满足grid[0][0] >0(因为max_val初始是0,而宝物价值已加1),所以如果起点的宝物价值是0(处理后的1),则大于0,可以拿。

这样,初始调用只需要一次,即dfs(0,0,0,0),因为递归函数内部处理了是否拿取当前格子的宝物。

但是,这样的处理是否正确?例如,小明在起点处选择不拿,那么后面的移动是否还能拿其他宝物?是的,因为max_val仍然是0,后面遇到比0大的宝物可以拿。

但如果小明在起点处选择拿,那么cnt变为1,max_val变为grid[0][0]。

这样,递归函数的设计是正确的。

现在,关于记忆化数组的初始化,需要将所有值初始化为-1,表示未访问过。在C++中,可以用memset或fill函数进行初始化。

此外,考虑到宝物的价值已经被加1处理,max_val的取值范围是0到13(因为原来的Ci最大是12,加1后最大是13)。

因此,记忆化数组的维度应该是n x m x (k+1) x 14。例如,在C++中,可以定义为:

int memo[50][50][13][14]; // n,m <=50; k<=12; max_val <=13.

但动态定义可能更灵活。或者,使用vector来定义四维数组。

例如:

vector<vector<vector<vector<int>>>> memo(n,
vector<vector<vector<int>>>(m,
vector<vector<int>>(k+1,
vector<int>(14, -1)
)
)
);

这样,在每次测试用例时,重新初始化memo数组为-1。

综上,结合以上思路,可以编写出DFS+记忆化的代码。但需要注意,这种方法在n和m较大时,可能会超时,因为状态数是50 * 50 * 12 * 14=420,000,每个状态需要计算多次,但可能还是可以接受的,尤其是在题目给定的时间限制下。

最后,需要将输入的宝物价值加1,并将初始max_val设为0(表示未拿任何宝物时的状态)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/35220.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式(行为型)-观察者模式

目录 定义 类图 角色 Subject&#xff1a;抽象主题&#xff08;抽象被观察者&#xff09;​ ConcreteSubject&#xff1a;具体主题&#xff08;具体被观察者&#xff09;​ Observer&#xff1a;抽象观察者​ ConcrereObserver&#xff1a;具体观察者​ 优缺点 优点​…

调用华为云API实现口罩识别

1.作者介绍 范小雨&#xff0c;女&#xff0c;西安工程大学电子信息学院&#xff0c;2024级研究生 研究方向&#xff1a;机器视觉与人工智能 电子邮件&#xff1a;1019044907qq.com 高金年&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2024级研究生&#…

C++中的单例模式及具体应用示例

AI 摘要 本文深入探讨了C中的单例模式及其在机器人自主导航中的应用&#xff0c;特别是如何通过单例模式来管理地图数据。文章详细介绍了单例模式的基本结构、优缺点以及在多线程环境中的应用&#xff0c;强调了其在保证数据一致性和资源管理中的重要性。 接着&#xff0c;文章…

【STM32】从新建一个工程开始:STM32 新建工程的详细步骤

STM32 开发通常使用 Keil MDK、STM32CubeMX、IAR 等工具来创建和管理工程。此处是 使用 Keil MDK5 STM32CubeMX 创建 STM32 工程的详细步骤。 新建的标准库工程文件已上传至资源中&#xff0c;下载后即可直接使用。 标准库新建 STM32 工程的基本目录结构&#xff1a;STD_STM…

Java 大视界 -- 基于 Java 的大数据实时流处理中的窗口操作与时间语义详解(135)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

Fastdata极数:中国民宿行业发展趋势报告2025

2024年&#xff0c;中国游客出行次数大幅上涨&#xff0c;旅游相关支出也复苏强劲。2025年中国旅游业还将持续稳健的复苏及增长。同时&#xff0c;中国旅游业将见证一场深刻的变革&#xff0c;这场变革的推动力是消费者对旅游期望的转变&#xff0c;经济因素和年轻人全新价值观…

【自定义微信小程序拉下选择过滤组件】searchable-select

【自定义微信小程序拉下选择过滤组件】searchable-select 组件说明 点击输入框获取焦点&#xff0c;输入内容&#xff0c;自动匹配搜索结果&#xff0c;点击搜索结果&#xff0c;自动填充搜索结果。 组件使用 将组件文件夹放在项目中。在需要使用的页面的json文件中&#x…

推理大模型的后训练增强技术-Reasoning模型也进化到2.0了,这次居然学会用工具了

论文题目&#xff1a;START: Self-taught Reasoner with Tools 论文链接&#xff1a;https://arxiv.org/pdf/2503.04625 论文简介 Reasoning模型也进化到2.0了&#xff0c;这次居然学会用工具了&#xff01;✨ 最近有个叫START的方法&#xff0c;让大模型也能学着用工具&#…

Idea集成docker通过ca加密实现镜像打包

​ Idea集成docker实现镜像打包_ideadocker镜像打包-CSDN博客 ​ 之前通过这种方式虽然可以实现idea通过maven打jar包的同时把docker镜像也进行打包&#xff0c;但是这种方式存在很大漏洞&#xff0c;就是服务器的2375端口大开&#xff0c;任何人拿着idea通过这种方式都可以连…

SOC与电压的关系

与电池相关的参数都与SOC有关&#xff0c;也就是电池剩余容量的百分比即荷电状态。 SOC百分之二十时&#xff0c;对应3.2V,SOC80&#xff05;时对应3.3V。

塔能科技:做节能界的“催化剂”,加速工厂能源改造变革

在全球坚定不移地迈向可持续发展的宏大进程中&#xff0c;节能降耗早已从一种发展理念&#xff0c;深度融入到经济社会发展的每一个脉络之中&#xff0c;成为企业在激烈市场竞争中实现降本增效的核心策略&#xff0c;更是推动整个社会朝着绿色、低碳、循环方向转型的关键支撑点…

【算法学习之路】11.并查集

并查集 前言一.简介二.基础并查集三.基础并查集题目12 四.种类并查集&#xff08;扩展域并查集&#xff09;五.种类并查集的题目 前言 我会将一些常用的算法以及对应的题单给写完&#xff0c;形成一套完整的算法体系&#xff0c;以及大量的各个难度的题目&#xff0c;目前算法也…

【微服务】SpringBoot整合LangChain4j 操作AI大模型实战详解

目录 一、前言 二、Langchain4j概述 2.1 Langchain4j 介绍 2.1.1 Langchain4j 是什么 2.1.2 主要特点 2.2 Langchain4j 核心组件介绍 2.3 Langchain4j 核心优势 2.4 Langchain4j 核心应用场景 三、SpringBoot 整合 LangChain4j 组件使用 3.1 前置准备 3.1.1 获取apik…

【图片批量转换合并PDF】多个文件夹的图片以文件夹为单位批量合并成一个PDF,基于wpf的实现方案

项目背景: 多个图片分布在不同文件夹,如何以文件夹为单位批量合并成一个PDF,还要保证文件夹里面图片大小和顺序 实现功能: 1、单张图片的转换PDF:一张图临时转一下 2、多张图片转换成PDF:多张图单独转成PDF 3、多级目录多张图转换成PDF:多级目录多张图单独转成多个PDF…

因果推荐|可解释推荐系统的反事实语言推理

论文&#xff1a;https://arxiv.org/pdf/2503.08051 代码&#xff1a;GitHub - kylokano/CausalX 很新的论文&#xff0c;南大五天前挂到arxiv的&#xff0c;代码基于Recbole&#xff0c;没给全但是提供了足够的验证。 1 动机 可解释推荐不仅提供高质量的推荐&#xff0c;而…

Zabbix安装(保姆级教程)

Zabbix 是一款开源的企业级监控解决方案,能够监控网络的多个参数以及服务器、虚拟机、应用程序、服务、数据库、网站和云的健康状况和完整性。它提供了灵活的通知机制,允许用户为几乎任何事件配置基于电子邮件的告警,从而能够快速响应服务器问题。Zabbix 基于存储的数据提供…

【spring boot 实现图片验证码 前后端】

导入hutool依赖 <!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.36</version>获取验证码接口 Autowiredprivate Captcha captcha;private final static Long VALIDA…

arthas基础命令

文章目录 1. help2. cat3. grep4. pwd5. cls6. session7. reset8. version9. history10. quit11. stop12. keymapArthas 命令行快捷键后台异步命令相关快捷键小结 1. help 作用&#xff1a;查看命令帮助信息 2. cat 作用&#xff1a;打印文件内容&#xff0c;和linux里的cat命…

痉挛性斜颈护理宝典:重拾生活平衡

痉挛性斜颈会给患者的生活带来诸多不便&#xff0c;有效的健康护理对缓解症状、提升生活质量十分关键。 在日常活动方面&#xff0c;患者应保持正确的姿势。站立和坐姿要挺直脊背&#xff0c;避免长时间低头或歪头&#xff0c;减少颈部肌肉的额外负担。睡眠时&#xff0c;选择高…

虚拟定位 1.2.0.2 | 虚拟定位,上班打卡,校园跑步模拟

Fake Location是一款运行于安卓平台上的功能强大、简单实用的虚拟定位软件。它能够帮助用户自定义位置到地图上的任意地方&#xff0c;以ROOT环境运行不易被检测&#xff0c;同时也支持免ROOT运行。提供路线模拟、步频模拟、WIFI模拟等方式&#xff0c;支持反检测。 大小&…