题解 CF118C Fancy Number

本题解核心思想:排序

首先肯定是要找到最小的代价,也就是输出第一行所需要的答案;

可以枚举达到 \(k\) 的数字,再按照每个数字与被枚举的数字的绝对差值排序,取前 \(k\) 个进行计算,这样可以轻松地把 \(res\) 求出;

再记录下 \(res\) 的同时可以记录下所有的可以达到 \(res\) 的数字,之后需要在其中寻找最小的字典序的排列

对于每个满足 \(res\) 的解法,要对其进行更改,具体过程代码中讲,然后再刷出满足的最小字典序

上代码

# include <bits/stdc++.h>
using namespace std;

struct reader {
	template <typename Type>
	reader &operator>> (register Type &ret) {
		register int f = 1; ret = 0; register char ch = getchar ();
		for (;!isdigit (ch); ch = getchar ()) if (ch == '-') f=-f;
		for (; isdigit (ch); ch = getchar ()) ret = (ret << 1) + (ret << 3) + ch - '0';
		ret *= f; return *this;
	}
}fin; //快读

int N, K, xp;
struct QDS {
	int num, id;
	bool operator < (register const QDS & P) const {
		return abs (num - xp) < abs (P.num - xp) ||
			(abs (num - xp) == abs (P.num - xp) && num > P.num) ||
			(num == P.num && (num > xp ? id < P.id : id > P.id));
	}
    /*
    这里的排序非常细节
    第一行是排当前数字 xp 与数组内元素的大小的绝对差值
    第二行是当差值相同时取元素数值大小较大的放在前面,目的是当修改大的对字典序的贡献绝对比修改小的对字典序的贡献大(想想为什么)
    第三行是观察被改的数字与 xp 之间的大小关系,目的是当被改的大于 xp 时,修改较前面的比修改较后面的贡献大,反之亦然
    举个例子:
    80000008 可以改成 00000008 或是 80000000 时
    很明显 00000008 的字典序更小,所以大改小改前面的
    23333332 可以改成 33333332 或是 23333333 时
    很明显 23333333 的字典序更小,所以小改大改后面的
    */
} A[10005], B[10005];
int res, mid, rs[15];
int R[10005];

bool cmp (register const QDS & X, register const QDS & Y) {
	return X.id < Y.id;
}	//这里排序的作用是当修改处理结束之后再按照原本的顺序重组

int main () {
	fin >> N >> K; res = 2147483641;
	for (register int i = 1; i <= N; i++) A[i].num = getchar () - '0', A[i].id = i, R[i] = 10;
	for (xp = 0; xp <= 9; xp++) {
		sort (A + 1, A + 1 + N); register int now = 0;
		for (register int i = 1; i <= K; i++) now += abs (A[i].num - xp);
		if (now < res) res = now, mid = xp;
		rs[xp] = now;
	}	//这里是刷出 res
	printf ("%d\n", res);
	for (xp = 0; xp <= 9; xp++) {
		if (rs[xp] != res) continue;
		sort (A + 1, A + 1 + N);
		for (register int i = 1; i <= N; i++) B[i] = A[i];
		for (register int i = 1; i <= K; i++) A[i].num = xp;
		sort (A + 1, A + 1 + N, cmp);
		//这里就是排序的核心代码,将其修改后复原顺序,得出当修改结束的数字是 xp 时的最优解
		bool check = false;
		for (register int i = 1; i <= N; i++) {
			if (A[i].num < R[i]) {check = true;  break;}
			if (A[i].num > R[i]) {check = false; break;}
			continue;
		}	if (check == true)
		for (register int i = 1; i <= N; i++) R[i] = A[i].num;
		//这一块是不断刷最小字典序排列,直到刷到一个最小的排列方案
		for (register int i = 1; i <= N; i++) A[i] = B[i];
		// B 数组的主要用途就是保护 A 数组不被修改
	}
	
	for (register int i = 1; i <= N; i++) putchar (R[i] + '0');
	putchar (10);
	return 0;
}

Made By \(\operatorname{wheneveright}\)

posted @ 2021-04-12 19:01  XJ21  阅读(103)  评论(0)    收藏  举报