CF1031D 最小字典序路径
1 CF1031D 最小字典序路径
2 题目描述
给出一个大小 \(𝑛×𝑛\) 用小写英文字母填充的矩阵。只能修改这个矩阵中不超过 \(k\) 个字母。考虑所有从左上角到右下角的所有路径,这些路径从单元格移动到其相邻的单元格,向右或向下移动。每个路径都与由路径访问的单元格中的所有字母组成的字符串相关联。因此,每个字符串的长度为 \(2𝑛-1\)。在矩阵的最多 \(𝑘\) 个单元格中找到在更改字母后与路径相关联的最小字典序字符串。字典序排列是比较两个字符串中第一个不同字母的大小,如果 \(a\) 的第一个不同字母 \(b\) 的小,则字符串 \(𝑎\) 比字符串 \(b\) 小。
3 题解
这道题如果不加入更改元素这一操作,很容易能想出一个朴素算法:\(bfs\)。从左上角出发,每一步走向向右和向下中字典序更小的节点。如果两个位置字典序相同,则将这两个位置都推入队列中。容易发现:当整个地图都为同一个字母的时候,时间复杂度会很高。为了避免 \(TLE\),我们考虑剪枝:如果一个位置被走过,那么它所对应的最小值肯定已经走完了,不用再去走了。这样,\(bfs\) 的时间复杂度在最坏情况下是 \(O(n^2)\)。
现在我们再来考虑更改这一操作。我们观察,发现:如果可以更改 \(k\) 个字母,那么最终路径中至少前 \(k\) 个字母为 \(a\)。什么时候会比 \(k\) 个多呢?当我们的路径上本来就有 \(a\) 的时候,我们没必要把它再去变成 \(a\)。因此,我们可以考虑计算在更改 \(k\) 个字母的前提下,可以走到的最远的长度和这一长度对应的一些位置。然后,我们再从这些位置出发,执行 \(bfs\) 计算它们走到右下角所可能走出的最小字典序。\(dp\) 则类似于数字三角形:只需要把所有的 \(a\) 的权值改为 \(0\),其他位置改为 \(1\),然后常规 \(dp\) 即可。
4 代码(空格警告):
#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;
}