AcWing-4724. 靓号

链接:https://www.acwing.com/problem/content/4727/

某地区的车牌号由 n 位数字组成。

如果一个车牌号中包含至少 k 个相同的数字,那么这个车牌号就被称为靓号。

如果车主对自己的车牌号感到不满意,还可以花钱对其进行修改。

每修改其中的一位数字,所需花费的具体金额为该位上修改前数字与修改后数字之差的绝对值。

例如,设车牌号为 0100,将其中的第 2 位数字从 1 修改为 3,使得车牌号变为 0300,所需花费的金额为 |1−3|=2。

现在,给定一个车牌号,请你花费最小的金额,将其修改为一个靓号。

输出最小花费金额以及得到的靓号。

如果最小花费下可以得到的靓号不唯一,则优先选择字典序最小的那个。

输入格式

第一行包含两个整数 n,k。

第二行包含一个长度为 n 的由数字(0∼9)组成的字符串。

输出格式

第一行输出所需花费的最小金额。

第二行输出最小花费下可以得到的靓号,如果不唯一,则输出字典序最小的那个。

数据范围

前 5 个测试点满足 2≤n≤10。
所有测试点满足 2≤n≤1e4,2≤k≤n。

输入样例1:

6 5
898196

输出样例1:

4
888188

输入样例2:

3 2
533

输出样例2:

0
533

输入样例3:

10 6
0001112223

输出样例3:

3
0000002223

首先是求出最小花费。由于可知车牌上的每个数字的范围是0~9的整数,也就是修改后的数只可能是0~9,所以我们可以枚举0~9每一个数x并把它充当修改后的那m个数的值, 让这个数对当前车牌上的每个数相减并取绝对值,那么该情况的最小花费,就是取最小的那m个绝对值相加。最终答案的最小花费就是这枚举的十个数对应的最小花费中的最小的那个。

这题难点在于如何求字典序最小的。首先我们可以分析,对于两个修改成x花费相同的数,如果其中一个数大于x,一个小于x,那么我们应该先修改大于x的那个数。因为,修改大于x的那个数,那个数就会变成x,数值变小,字典序会变小,反之修改小于x的那个数,数值变大,字典序变大。所以应该先修改大于x的那个数。 

然后对于两个同时大于x或小于x并且与x的差值的绝对值相同(即花费相同)的数,如果两个数同时大于x,那么我们应该先修改下标小的那个,因为下标小的那个数变小,和下标大的那个数变小相比,对字典序变小的贡献更大,如:对于车牌24542,要把4变成3,明显23542比24532的字典序要小。

同理对于两个数同时小于x,那么我们则应该先修改下标大的那个, 因为下标大的那个数变大,比下标小的那个数变大,对字典序变大的贡献更小。如:车牌24542,要把2变3,24543比34542的字典序更小。

操作时,我们可以开个vector数组,用数组的下标当作差值的大小,把车牌上的每个数的位置都存到其与x的差值的对应下标的位置。由于差值的范围是-9~9,数组下标不能为负数,所以可以进行映射,让0~18对应-9~9。 修改时我们先从差值绝对值小的部分开始,然后根据上面分析的修改顺序,修改m个数即可。 

代码如下:

#include<iostream>
#include<string>
#include<vector>
using namespace std;
const int N = 1e4+5;
int minv = 0x3f3f3f3f;
string res, ss; //res是最终结果的字符串,ss是输入的字符串 
int n, m; //n是字符串长度,m是连续的相同数字个数 
void solve(char x)
{
    vector<int> v[20]; //v[i]用来存与x相减差值为i的下标
	string s = ss; //复制一遍,方便修改 
	for(int i=0; i<s.size(); i++)
		v[s[i]-x+9].push_back(i); //因为数组下标不能为负数,所以进行映射,0~8表示差值为-9~-1的,9~18存差值为0~9的 
	
	int cnt = 0, cost = 0; //cnt用来存当前有多少个相同的数了,cost表示当前花费 
	for(int k=0; cnt<m; k ++) //k表示差值的绝对值
	{
		for(int j=0; j<v[k+9].size() && cnt<m; j++) //先改比x大的,先改下标在前的 
		{
			s[v[k+9][j]] = x;
			cost += k;
			cnt ++;
		}
	
		if(k) //如果k>0说明,把所有差值为0的下标全部修改完后,不存在m个相同的数 
		{
			for(int j=v[9-k].size()-1; j>=0 && cnt<m; j --) //改比x小的,先改下标在后的 
			{
				s[v[9-k][j]] = x;
				cost += k;
				cnt ++;
			}
		}
	}
	if(cost<minv || cost==minv && s<res) //如果cost的值比minv要小,或者值相同但s的字典序更小,则更新 
	{
		minv = cost;
		res = s;
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> n >> m >> ss;
	for(int i=0; i<=9; i++) //枚举修改后的m个相同的数
		solve(i+'0');

	cout << minv << "\n" << res << endl;
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值