1. 程式人生 > >Codeforces Round #517 (Div. 2, based on Technocup 2019 Elimination Round 2) D. Minimum path(字典序)

Codeforces Round #517 (Div. 2, based on Technocup 2019 Elimination Round 2) D. Minimum path(字典序)

https://codeforces.com/contest/1072/problem/D

題意

給你一個n*n充滿小寫字母的矩陣,你可以更改任意k個格子的字元,然後輸出字典序最小的從[1,1]到[n,n]的路徑(1<=n<=2000)

思路

我的

  • 定義dp[i][j]為從[1,1]到[i,j]字串最小的字串,然後列舉所有i-1+j-1+1<=k的點,然後字串連線比較,代表更改前面i,j個字元
  • 這種思路有兩個問題,如何處理出從[i,j]到[n,n]的最小字典序字串,第二假如i+j個字元中存在'a'的話那麼修改次數可以往後使用,這兩點都是這個思路沒有考慮到的

標解

  • 我們如何得到字典序最小的字串?(逐位確定法)
    • 固定了步數,我們就可以知道所有這個步數能到達的格子。然後我們列舉步數就等於在模擬走的過程,需要縱向比較所有當前步數所能到達的格子的字元大小,選取最小的繼續走,比他大的就不用繼續了,這樣就能保證得到的字串是最小的
  • 怎麼處理最多隻能修改k個格子?
    • 假如這個格子是'a',那麼這個格子就不用再修改
    • 而對於一條還能修改的路徑來說,越多的a,意味著剩下還能修改的格子越多,
    • 定義dp[i][j]代表到i,j最少a的路徑a的數量,
    • dp[i][j]=min(dp[i-1][j],dp[i][j-1])+(s[i][j]!='a');
    • 假如dp[i][j]<=k,那麼s[i][j]可以修改為a
#include<bits/stdc++.h>
#define M 2005
using namespace std;
int n,k,i,j,p,vi[M][M],f[M][M];
char s[M][M],mi;

int main(){
    cin>>n>>k;
    for(i=1;i<=n;i++)scanf("%s",s[i]+1);
    memset(f,1,sizeof(f));
    f[0][1]=0;
    for(i=1;i<=n;i++)for(j=1;j<=n;j++){
        f[i][j]=min(f[i-1][j],f[i][j-1])+(s[i][j]=='a'?0:1);
        if(f[i][j]<=k)s[i][j]='a';
    }
    vi[1][1]=1;
    for(p=2;p<=2*n;p++){
        mi='z';
        for(i=1;i<=n;i++)if(p-i>=1&&p-i<=n&&vi[i][p-i])mi=min(mi,s[i][p-i]);
        putchar(mi);
        for(i=1;i<=n;i++)if(p-i>=1&&p-i<=n&&vi[i][p-i]&&s[i][p-i]==mi)
            vi[i+1][p-i]=vi[i][p-i+1]=1;
    }
}