Loading

题解 CF1428G Lucky Numbers (Easy Version and Hard Version)

这题没有压行就成 \(\texttt{Hard Version}\) 最短代码解了(

要知道这题那么 \(sb\) 就不啃 \(D\)\(E\) 了。

\(\texttt{Solution}\)

首先有一个非常简单但是错误的多重背包的想法:

让分拆出来的 \(k\) 个数中,每一个数在十进制下每一位都是 \(0, 3, 6\)\(9\),于是对于第 \(x\) 位把 \(3k\) 个大小为 \(3 \times 10^x\), 价值为 \(F_x\) 的物品丢进多重背包里面,然后输出答案。

这样子显然是不对的,例如输入的数不是 \(3\) 的倍数就被卡掉了。因为还可能在某一位上价值是 \(0\)。然后考虑贪心,我们肯定要让这些位置上不是 \(0, 3, 6, 9\) 的位置越少越好。

有一个很显然的结论:一定可以让某一位上价不是 \(0, 3, 6, 9\) 的个数减少到 \(1\)。证明:如果有两个非 \(0, 3, 6, 9\) 的数 \(a\)\(b\)。如果 \(a + b > 9\), 那么可以变成 \(9\)\(a+b-9\); 否则可以变成 \(0\)\(a+b\)

那么我们只要对这不是 \(0, 3, 6, 9\) 的那些位置进行特殊处理即可。为了方便,我们把这些位放在同一个数上。可以对于这些数提前统计他们的价值。然后对于剩下 \(k-1\) 个数,按照前面所提到的错误做法,对于第 \(x\) 位把 \(3(k-1)\) 个大小为 \(3 \times 10^x\), 价值为 \(F_x\) 的物品丢进多重背包里面。但是这样子会 \(TLE\), 然后改成二进制优化多重背包即可。不会二进制优化背包?毙了吧

\(\texttt{Code}\)

#include<bits/stdc++.h>
using namespace std;
#define L(i, j, k) for(int i = (j); i <= (k); i++) 
#define R(i, j, k) for(int i = (j); i >= (k); i--) 
#define ll long long 
const int N = 1e6 + 7; 
int n, m, k, sz, t, q;
ll p[6], f[N];
void Push(int v, ll w) { R(i, 1e6, v) f[i] = max(f[i], w + f[i - v]); }
void gg(int v, int w) {
	int now = min(k, (int)1e6 / v);
	for(int i = 1; i < now; i <<= 1) now -= i, Push(v * i, 1ll * w * i); 
	Push(v * now, 1ll * w * now);
}
int main() {
	scanf("%d", &k), sz = 1, k = 3 * (k - 1);
	L(i, 0, 5) scanf("%d", &p[i]);
	L(i, 0, 1e6) {
		int now = 0, x = i, s = x % 10;
		while(x) {
			if(s % 3 == 0) f[i] += 1ll * p[now] * (s / 3);
			x /= 10, ++now, s = x % 10;
		}
	}
	L(i, 0, 5) gg(sz * 3, p[i]), sz *= 10;
	scanf("%d", &q);
	while(q--) scanf("%d", &t), printf("%lld\n", f[t]);
	return 0;
}
posted @ 2020-10-18 12:19  zhoukangyang  阅读(633)  评论(0编辑  收藏  举报