题目链接
题意:给定初始的字符串,由数字组成,问如果必须删除k位数字,剩下的数最小为多少,要保证剩下的数不含前导0。
关键是对题目进行转化,删去k个数,实际上是选取n-k个数保留,那保留的策略是什么呢?贪心。优先选取尽量小的数,感觉难点在于怎么优雅的用代码去实现,下面给出官方题解的代码,具体思路在代码中。
这种优雅的写法很值得学习。
code:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;
const int N=200+10;
int a[N];
void work()
{
/*
本题难点在于如何思路转换和 优雅的模拟
把删除K个数,转化为贪心的选取n-k个数,同时要满足潜在的约束条件
贪心思路为,从最高位开始选,从前往后选,从数字0开始,若可以选取,就选
若无法选取,则从1.2.3 等继续选。
若选取了这个数字,表示当前选的这个和前一个选的之间的数字要全部删去,
同时要满足删除的某一段要满足不小于当前k值
*/
string s;
int k;
cin>>s>>k;
vector<int> pos[20];
int n=s.size();
for(int i=0;i<n;i++){
pos[s[i]-'0'].push_back(i);
}
for(int i=0;i<=9;i++) reverse(pos[i].begin(),pos[i].end());
string ans="";
int last=0,len=n-k;
//last为前一个选到的数的位置,的下一个位置
//k为当前可以删除的数的数量
for(int i=0;i<len;i++){// 贪心的选取len个数
for(int j=0;j<=9;j++){
if(i==0&&j==0) continue; //第一个数不能选0
while(pos[j].size()&&pos[j].back()<last){
pos[j].pop_back();
}
if(pos[j].size()&&pos[j].back()-last<=k){
ans+=j+'0';
k-=pos[j].back()-last;//表示这一段要删除,因此k也要减少
last=pos[j].back()+1;
break; //注意这里要break,因为本轮已经找到了,
}
}
}
cout<<ans<<endl;
}
signed main()
{
int t;
cin>>t;
//t=1;
while(t--) work();
return 0;
}
还有类似于这种选数的题:
见这篇文章的C题。
也是贪心的选数。