CF1031D 最小字典序路径

1 CF1031D 最小字典序路径

2 题目描述

给出一个大小 𝑛×𝑛 用小写英文字母填充的矩阵。只能修改这个矩阵中不超过 k 个字母。考虑所有从左上角到右下角的所有路径,这些路径从单元格移动到其相邻的单元格,向右或向下移动。每个路径都与由路径访问的单元格中的所有字母组成的字符串相关联。因此,每个字符串的长度为 2𝑛1。在矩阵的最多 𝑘 个单元格中找到在更改字母后与路径相关联的最小字典序字符串。字典序排列是比较两个字符串中第一个不同字母的大小,如果 a 的第一个不同字母 b 的小,则字符串 𝑎 比字符串 b 小。

3 题解

这道题如果不加入更改元素这一操作,很容易能想出一个朴素算法:bfs。从左上角出发,每一步走向向右和向下中字典序更小的节点。如果两个位置字典序相同,则将这两个位置都推入队列中。容易发现:当整个地图都为同一个字母的时候,时间复杂度会很高。为了避免 TLE,我们考虑剪枝:如果一个位置被走过,那么它所对应的最小值肯定已经走完了,不用再去走了。这样,bfs 的时间复杂度在最坏情况下是 O(n2)

现在我们再来考虑更改这一操作。我们观察,发现:如果可以更改 k 个字母,那么最终路径中至少前 k 个字母为 a。什么时候会比 k 个多呢?当我们的路径上本来就有 a 的时候,我们没必要把它再去变成 a。因此,我们可以考虑计算在更改 k 个字母的前提下,可以走到的最远的长度和这一长度对应的一些位置。然后,我们再从这些位置出发,执行 bfs 计算它们走到右下角所可能走出的最小字典序。dp 则类似于数字三角形:只需要把所有的 a 的权值改为 0,其他位置改为 1,然后常规 dp 即可。

4 代码(空格警告):

Copy
#include <iostream> #include <vector> #include <queue> #include <cstring> using namespace std; const int N = 2005; int n, k, maxn, cnt; int dp[N][N]; char Map[N][N], minn, ans[N*2]; bool f[N][N]; string a; vector< pair<int, int> > v; queue< pair<int,int> > q; int main() { cin >> n >> k; for (int i = 1; i <= n; i++) { cin >> a; for (int j = 1; j <= n; j++) Map[i][j] = a[j-1]; } if (Map[1][1] != 'a') dp[1][1] = 1; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (i == 1 && j == 1) continue; if (j-1 >= 1 && i-1 >= 1) dp[i][j] = min(dp[i][j-1], dp[i-1][j]); else if (j-1 >= 1) dp[i][j] = dp[i][j-1]; else if (i-1 >= 1) dp[i][j] = dp[i-1][j]; if (Map[i][j] != 'a') dp[i][j]++; } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (dp[i][j] <= k) { if (maxn < i+j) { v.clear(); v.push_back(make_pair(i, j)); maxn = i+j; } else if (i + j == maxn) v.push_back(make_pair(i, j)); } } } if (k == 0) { f[1][1] = 1; v.clear(); v.push_back(make_pair(1, 1)); ans[++cnt]=Map[1][1]; maxn = 2; } for (int i = 0; i < v.size(); i++) q.push(v[i]); for (int i = maxn; i < 2*n; i++) { v.clear(); minn = 'z'; while (q.size()) { int x = q.front().first, y = q.front().second; q.pop(); if (x + 1 <= n && !f[x+1][y]) { if (Map[x+1][y] < minn) { minn = Map[x+1][y]; v.clear(); v.push_back(make_pair(x+1, y)); f[x+1][y] = 1; } else if (Map[x+1][y] == minn) { v.push_back(make_pair(x+1, y)); f[x+1][y] = 1; } } if (y + 1 <= n && !f[x][y+1]) { if (Map[x][y+1] < minn) { minn = Map[x][y+1]; v.clear(); v.push_back(make_pair(x, y+1)); f[x][y+1] = 1; } else if (Map[x][y+1] == minn) { v.push_back(make_pair(x, y+1)); f[x][y+1] = 1; } } } for (int i = 0; i < v.size(); i++) q.push(v[i]); ans[++cnt] = minn; } if (k) for (int i = 1; i < maxn; i++) cout << 'a'; for (int i = 1; i <= cnt; i++) cout << ans[i]; return 0; }

欢迎关注我的公众号:智子笔记

posted @   David24  阅读(341)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示