Minimum path (Codeforces 1072D)
Tags: Greedy, dfs and similar

You are given a matrix of size $$$n \times n$$$ filled with lowercase English letters. You can change no more than $$$k$$$ letters in this matrix.Consider all paths from the upper left corner to the lower right corner that move from a cell to its neighboring cell to the right or down. Each path is associated with the string that is formed by all the letters in the cells the path visits. Thus, the length of each string is $$$2n - 1$$$.Find the lexicographically smallest string that can be associated with a path after changing letters in at most $$$k$$$ cells of the matrix.A string $$$a$$$ is lexicographically smaller than a string $$$b$$$, if the first different letter in $$$a$$$ and $$$b$$$ is smaller in $$$a$$$.

Input: The first line contains two integers $$$n$$$ and $$$k$$$ ($$$1 \le n \le 2000$$$, $$$0 \le k \le n^2$$$) — the size of the matrix and the number of letters you can change.Each of the next $$$n$$$ lines contains a string of $$$n$$$ lowercase English letters denoting one row of the matrix.

Output: Output the lexicographically smallest string that can be associated with some valid path after changing no more than $$$k$$$ letters in the matrix.

Note: In the first sample test case it is possible to change letters 'b' in cells $$$(2, 1)$$$ and $$$(3, 1)$$$ to 'a', then the minimum path contains cells $$$(1, 1), (2, 1), (3, 1), (4, 1), (4, 2), (4, 3), (4, 4)$$$. The first coordinate corresponds to the row and the second coordinate corresponds to the column.

Sample Inputs:
4 2
abcd
bcde
bcad
bcde

5 3
bwwwz
hrhdh
sepsp
sqfaf
ajbvw

7 6
ypnxnnp
pnxonpm
nxanpou
xnnpmud
nhtdudu
npmuduh
pmutsnz

Sample Outputs:
aaabcde

aaaepfafw

aaaaaaadudsnz

Solution (Python):
(n, k) = map(int, input().split())
strings = []
for i in range(n):
    strings.append(input())
minswap = []
if strings[0][0] == 'a':
    minswap.append([0])
else:
    minswap.append([1])
for i in range(n - 1):
    new = []
    if strings[0][i + 1] == 'a':
        new.append(minswap[-1][0])
    else:
        new.append(minswap[-1][0] + 1)
    for j in range(i):
        if strings[j + 1][i - j] == 'a':
            new.append(min(minswap[-1][j + 1], minswap[-1][j]))
        else:
            new.append(min(minswap[-1][j + 1], minswap[-1][j]) + 1)
    if strings[i + 1][0] == 'a':
        new.append(minswap[-1][-1])
    else:
        new.append(minswap[-1][-1] + 1)
    minswap.append(new)
for i in range(n - 1):
    new = []
    for j in range(n - 1 - i):
        if strings[i + j + 1][n - j - 1] == 'a':
            new.append(min(minswap[-1][j], minswap[-1][j + 1]))
        else:
            new.append(min(minswap[-1][j], minswap[-1][j + 1]) + 1)
    minswap.append(new)
farthest = -2
for i in range(2 * n - 1):
    reach = False
    for guy in minswap[i]:
        if guy <= k:
            reach = True
            break
    if not reach:
        farthest = i - 1
        break
if farthest == -2:
    farthest = 2 * n - 2
gis = []
if farthest >= 0:
    out = 'a' * (farthest + 1)
    row = farthest + 1
    for i in range(len(minswap[row - 1])):
        if minswap[row - 1][i] == k:
            gis.append(i)
else:
    out = strings[0][0]
    row = 1
    gis = [0]
while row < 2 * n - 1:
    if row < n:
        letters = []
        for index in gis:
            letters.append(strings[index][row - index])
            letters.append(strings[index + 1][row - index - 1])
        low = min(letters)
        newgis = []
        for index in gis:
            if strings[index][row - index] == low:
                newgis.append(index)
            if strings[index + 1][row - index - 1] == low:
                newgis.append(index + 1)
        out += low
        gis = list(set(newgis))
        row += 1
    else:
        letters = []
        for index in gis:
            if index not in {0, 2 * n - 1 - row}:
                letters.append(strings[row + index - n][n - index])
                letters.append(strings[row + index + 1 - n][n - 1 - index])
            elif index == 0:
                letters.append(strings[row + 1 - n][-1])
            else:
                letters.append(strings[-1][row + 1 - n])
        low = min(letters)
        newgis = []
        for index in gis:
            if index not in {0, 2 * n - 1 - row}:
                if strings[row + index - n][n - index] == low:
                    newgis.append(index - 1)
                if strings[row + index + 1 - n][n - 1 - index] == low:
                    newgis.append(index)
            elif index == 0:
                if strings[row + 1 - n][-1] == low:
                    newgis.append(index)
            elif strings[-1][row + 1 - n] == low:
                newgis.append(index - 1)
        out += low
        gis = list(set(newgis))
        row += 1
print(out)

Solution (Python):
(n, k) = map(int, input().split())
arr = [['zz' for j in range(n + 2)]]
arr2 = [[j * 0 + 10 ** 9 for j in range(n + 2)]]
good = [[j * 0 for j in range(n + 2)]]
for i in range(n):
    arr.append(['zz'] + list(input()) + ['zz'])
    arr2.append([j * 0 + 10 ** 9 for j in range(n + 2)])
    good.append([j * 0 for j in range(n + 2)])
good.append([j * 0 for j in range(n + 2)])
arr.append(['zz' for j in range(n + 2)])
arr2[0][1] = 0
good[1][1] = 1
for i in range(1, n + 1):
    for j in range(1, n + 1):
        arr2[i][j] = min(arr2[i][j - 1], arr2[i - 1][j]) + int(arr[i][j] != 'a')
        if arr2[i][j] <= k:
            arr[i][j] = 'a'
ans = ''
for i in range(2, n * 2):
    min_letter = 'z'
    for j in range(1, n + 1):
        if i - j > 0 and i - j < n + 1 and (good[j][i - j] == 1):
            min_letter = min(min_letter, arr[j][i - j])
    ans += min_letter
    for j in range(1, n + 1):
        if i - j > 0 and i - j < n + 1 and (good[j][i - j] == 1) and (arr[j][i - j] == min_letter):
            good[j][i - j + 1] = 1
            good[j + 1][i - j] = 1
print(ans + arr[n][n])

Solution (Python):
def main():
    (n, k) = map(int, input().split())
    line = list()
    for i in range(n):
        line.append([ord(elem) for elem in input()])
    dp = [[0] * n for i in range(n)]
    pos = [(0, 0)]
    max_cnt = -1
    for i in range(n):
        for j in range(n):
            if i == 0:
                dp[i][j] = dp[i][j - 1]
            elif j == 0:
                dp[i][j] = dp[i - 1][j]
            else:
                dp[i][j] = min(dp[i - 1][j], dp[i][j - 1])
            if line[i][j] != 97:
                dp[i][j] += 1
            if dp[i][j] <= k:
                if i + j > max_cnt:
                    max_cnt = i + j
                    pos = [(i, j)]
                elif i + j == max_cnt:
                    pos.append((i, j))
    if max_cnt == -1:
        ans = [chr(line[0][0])]
    else:
        ans = ['a' for i in range(max_cnt + 1)]
    pos_2 = set()
    while True:
        min_w = 130
        for (i, j) in pos:
            if i + 1 == n and j + 1 == n:
                print(''.join(ans))
                return
            if j + 1 != n:
                if line[i][j + 1] < min_w:
                    min_w = line[i][j + 1]
                    pos_2 = set()
                    pos_2.add((i, j + 1))
                elif line[i][j + 1] == min_w:
                    pos_2.add((i, j + 1))
            if i + 1 != n:
                if line[i + 1][j] < min_w:
                    min_w = line[i + 1][j]
                    pos_2 = set()
                    pos_2.add((i + 1, j))
                elif line[i + 1][j] == min_w:
                    pos_2.add((i + 1, j))
        ans.append(chr(min_w))
        pos = list(pos_2)
main()