VIrtuoso

两把多兰剑加个布甲鞋

导航

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;
    }
}



posted on 2018-11-12 14:23  VIrtuoso  阅读(92)  评论(0编辑  收藏  举报