题目链接:Codeforces - Minimum path
最小字典序显然可以贪心。
我们首先找到使用k次机会,的最大 x+y 的位置,很明显的贪心。现在可能有很多点,要怎么走呢?
我们判断当前所有点的出点,然后存到vector里面,按照字典序排序。因为贪心法则,只有等于第一个字符的才会走,而且一个点只会被更新一次。
所以暴力走然后sort即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e3+10;
int n,k,sum[N][N],mx,vis[N][N];
char g[N][N]; pair<int,int> pre[N][N];
vector<pair<int,int> > v;
struct node{int x,y,p;}; vector<node> q;
int cmp(node a,node b){return a.p<b.p;}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++) scanf("%s",g[i]+1);
if(k&&g[1][1]!='a') k--,g[1][1]='a';
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
sum[i][j]=max(sum[i-1][j],sum[i][j-1])+(g[i][j]=='a');
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) if(i+j-1<=sum[i][j]+k&&i+j>=mx){
if(i+j>mx) v.clear(),v.push_back({i,j}),mx=i+j;
else v.push_back({i,j});
}
}
if(v.size()==0) v.push_back({1,1}),cout<<g[1][1];
for(int i=0;i<v.size();i++){
int x=v[i].first,y=v[i].second;
if(x+1<=n&&!vis[x+1][y])
vis[x+1][y]=1,pre[x+1][y]={x,y},q.push_back({x+1,y,(int)g[x+1][y]});
if(y+1<=n&&!vis[x][y+1])
vis[x][y+1]=1,pre[x][y+1]={x,y},q.push_back({x,y+1,(int)g[x][y+1]});
}
while(q.size()){
sort(q.begin(),q.end(),cmp);
int x=q[0].x,y=q[0].y; vector<node> t;
if(x+1<=n&&!vis[x+1][y])
vis[x+1][y]=1,pre[x+1][y]={x,y},t.push_back({x+1,y,(int)g[x+1][y]});
if(y+1<=n&&!vis[x][y+1])
vis[x][y+1]=1,pre[x][y+1]={x,y},t.push_back({x,y+1,(int)g[x][y+1]});
for(int i=1;i<q.size();i++){
if(q[i].p>q[i-1].p) break;
x=q[i].x,y=q[i].y;
if(x+1<=n&&!vis[x+1][y])
vis[x+1][y]=1,pre[x+1][y]={x,y},t.push_back({x+1,y,(int)g[x+1][y]});
if(y+1<=n&&!vis[x][y+1])
vis[x][y+1]=1,pre[x][y+1]={x,y},t.push_back({x,y+1,(int)g[x][y+1]});
}
q.clear();
for(int i=0;i<t.size();i++) q.push_back(t[i]);
t.clear();
}
for(int i=1;i<mx;i++) putchar('a');
stack<char> res; int x=n,y=n;
while(x!=0){
int tx=x,ty=y;
res.push(g[x][y]); x=pre[tx][ty].first,y=pre[tx][ty].second;
}
res.pop();
while(res.size()) cout<<res.top(),res.pop();
return 0;
}