CodeForces 1373 - E. Sum of Digits - 贪心 + 构造

题目传送门:E. Sum of Digits

题目大意

f[x] 为x十进制表示的数码和,给定n, k找到最小的x使得f[x] + f[x + 1] + ... + f[x + k] = n (1 \leqslant n \leqslant 150, 0 \leqslant k \leqslant 9)

思路

本题做法有很多,比如本题n和k都很小,我们可以打表

这里我讲我过的方法(后面可能会补上一些其他方法),贪心 + 构造

贪心的要点在于:进位只需进到十位,如果有更高位的进位,如9999 -> 10000,数码和为37,那么一定存在更优的x比9999小,比如198 -> 199

构造的要点在于:对于某个个位,如果需要进位,就构造成x9...98y的形式,如果不用进位就构造成x9...9y的形式,当然中间的9和8可以省略的,比如n比较小,答案可能就是xy的形式(x可以为0)

因为我们希望找到最小的x,所以尽可能多的使用9使得位数尽可能少,并且使最高位尽可能小(这是显然的)

用上述方法,我们只要对于x的每个个位构造一个答案,取最小即可。

既然是贪心,那么我们肯定要证明贪心的正确性,但是我不会证(证到一半卡壳了,毕竟我菜,大家可以证证看,毕竟数据范围挺小能过应该是对的)。

代码

#include 
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair PII;
typedef pair pll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
const ll INF = (1ll << 62) + (1ll << 61);
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
int a[20];
int main()
{
	a[0] = 0;
	for (int i = 1; i <= 9; ++ i) a[i] = a[i + 9] = i;//这里把进位的1也加上了
	int t;
	cin >> t;
	while (t --){
		int n, k;
		cin >> n >> k;
		ll ans = INF;
		for (int i = 0; i <= 9; ++ i){//枚举x的个位
			int sum = 0, m = n;
			ll res = 0;
			for (int j = i; j <= i + k; ++ j) sum += a[j];//计算个位数码和和进位的1
			m -= sum;
			if (m < 0 || m % (k + 1)) continue;
			int x = m / (k + 1);//分成k + 1份
            //十位进位多贡献的1在计算个位数码和时算了,这里平分即可
			if (x < 9) res = x;
			else {
				int tmp = 8;
				if (i + k <= 9) tmp = 9;//个位为i时不需要进位,那么第二种构造
				x -= tmp;
				res = x % 9;//首位
				x -= res;
				while (x){
					res = res * 10 + 9;//其余用9
					x -= 9;
				}
				res = res * 10 + tmp;//加上十位
			}
			res = res * 10 + i;//加上个位
			ans = min(ans, res);//取最小
		}
		if (ans == INF) cout << -1 << '\n';
		else cout << ans << '\n';
	}
	return 0;
}

 

 

你可能感兴趣的:(c++,贪心算法)