版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Qin7_Victory/article/details/78827954
一个车牌号由n位数字组成。如果一个车牌至少有k位数字是相同的,那么我们就说这个车牌漂亮的车牌。现在华沙想要改变他自己的车牌,使得他的车牌变得漂亮。当然,改车牌是要花钱的。每改变一位数字所要花费的费用等于当前位上的新旧数字之差的绝对值。那么总费用就是每位上所花费用的总和。
举例如下,
旧牌为0123,新牌为7765,那么对应第一位所花费用为|0-7|=7,第二位为|1-7|=6,第三位为|2-6|=4,第四位为|3-5|=2,总和为7+6+4+2=19
华沙想用最少的钱,使他的车牌变得漂亮起来。现在给定n,k,和旧牌的号码,计算换牌的最少费,以及新牌的号码,
如果最少费用的号码有多个,我们取字典序最小的那个。
样例解释:
在样例中,把第二个数字换成“8”花费|9-8|=1,把第五个数字换成“8”也花了1。
把第六个数字换成“8”花费|6-8|=2.总费用为1+1+2=4,新号码为“888188”
两个长度为n的序列比较方法如下。
存在两个序列x,y,长度都是n。
如果存在i(1≤i≤n)和任意j(1≤j<i)使得 xi<yi
并且 xj=yj
,那么我们就说x比y小。
Input
单组测试数据
第一行,两个由空格隔开的数字n和k(2≤n≤10^4,2≤k≤n),表示旧牌的位数,和至少要有k位数字相同才能构成漂亮的车牌。
第二行有n位数字,代表华沙的旧车牌。(旧车牌中只有数字)。
Output
共两行,
第一行,一个整数,代表换牌的最小费用,
第二行,n位数字,表示新的车牌。
如果最小费用的车牌有多个,输出字典序最小的那个。
Sample Input
6 5
898196
Sample Output
4
888188
想着用for循环,从0到9。然后找到差值和最小的。需要值得注意的是 1 找差值和最小的 2 如果最小值相等,那么应该根据字符串比大小,找出符合题意,并且最小的字符串。
在这里,我们应该尝试着先从后往前,将小的数给换掉,从前往后,将大的数给换掉。这样进行两次遍历,是为了避免如果存在多个相同的数,而将前面小的值换成了大的,数值越变越大
for(int i = n-1; i >= 0; i--)
{
int j = s2[i]-'0';
if(c[j] < b[j] && j < k)
{
s2[i] = (k + '0');
c[j]++;
}
}
for(int i = 0; i < n; i++)
{
int j = s2[i] - '0';
if(c[j] < b[j] && j > k)
{
s2[i] = (k + '0');
c[j]++;
}
}
for(int k = 0; k < n; k++) //从前往后,表示将小的替换大的
if(p && t[k] - 48 == i+j) //i+j 中 i 表示替换的数,j表示差值
{
p--;
sum += j;
t[k] = i + 48;
}
if(j) //由于从前往后遍历的时候,已经将是本身的数给减去过了
for(int k = n-1; k >= 0; k--) //把相差值为j 但是比原来的值大,所以放后面,故从后往前
if(p && t[k]-48 == i-j)
{
p--;
t[k] = i + 48;
sum += j;
}
下面是两种方法
1 暴力法
#include<stdio.h>
#include<string.h>
#include<string>
#include<iostream>
using namespace std;
int main()
{
int n,m,p,sum,minn = 100000000;
string s,t,ans;
cin >> n >> m >> s;
for(int i = 0; i < 10; i++)
{
t = s,p = m, sum = 0;
for(int j = 0; j < 10; j++)
{
for(int k = 0; k < n; k++) //从前往后,表示将小的替换大的
if(p && t[k] - 48 == i+j) //i+j 中 i 表示替换的数,j表示差值
{
p--;
sum += j;
t[k] = i + 48;
}
if(j) //由于从前往后遍历的时候,已经将是本身的数给减去过了
for(int k = n-1; k >= 0; k--) //把相差值为j 但是比原来的值大,所以放后面,故从后往前
if(p && t[k]-48 == i-j)
{
p--;
t[k] = i + 48;
sum += j;
}
}
if(sum < minn)
{
minn = sum;
ans = t;
}
if(sum == minn)
ans = min(ans,t);
}
cout << minn << endl;
cout << ans << endl;
return 0;
}
2 从相差数值从0到9开始 前后找
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
char s[10010],s1[10010],s2[10010];
int m,n;
int a[20],b[20],c[20];
int main()
{
while(~scanf("%d%d",&n,&m))
{
scanf("%s",s);
memset(a,0,sizeof(a));
for(int i = 0; i < n; i++)
a[s[i]-'0']++;
int minn = 9999999999;
int t,p,q;
for(int k = 0; k < 10; k++)
{
p = k-1; //将p给替换掉
q = k+1; //将q给替换掉
t = m-a[k]; //需要替换几个数字
if(t <= 0)
{
strcpy(s1,s);
minn = 0;
break;
}
memset(b,0,sizeof(b)); //标记,记录替换数字的个数
int sum = 0;
while(1)
{
if(p < 0 && q > 9)
break;
if(q <= 9) //将大的数字给替换掉
{
if(t >= a[q]) //数字个数少于t
{
b[q] = a[q];
t -= a[q];
sum += (q-k)*a[q]; //统计相差值得和
}
else
{
b[q] = t;
sum += (q-k)*t;
t = 0; //达到题目要求
}
}
if(p >= 0) //将小的数字给替换
{
if(t >= a[p])
{
t -= a[p];
sum +=(k-p)*a[p];
b[p] = a[p];
}
else
{
b[p] = t;
sum += (k-p)*t;
t = 0;
}
}
p--; //相差值逐渐变大
q++; //
}
if(sum <= minn)
{
strcpy(s2,s);
memset(c,0,sizeof(c));
for(int i = n-1; i >= 0; i--) //先从后往前找,将小的数给换掉,以至于变换过之后,数值变换不会太大
{
int j = s2[i]-'0';
if(c[j] < b[j] && j < k) //一定要写j < k 而不能将这两层for循环合并的原因是,假如上面的for循环中
{
s2[i] = (k + '0'); //在p <= 9 或者 q >= 0 中执行了else语句,则表示j的数多,应该从后往前替换
c[j]++;
}
}
for(int i = 0; i < n; i++) //在从前往后找,将大的数值给替换掉,使数值整体变小
{
int j = s2[i] - '0';
if(c[j] < b[j] && j > k)
{
s2[i] = (k + '0');
c[j]++;
}
}
if(minn > sum)
{
minn = sum;
strcpy(s1,s2);
}
else if(strcmp(s1,s2)>0)
strcpy(s1,s2);
}
}
printf("%d\n%s\n",minn,s1);
}
return 0;
}