20240912 随机训练
Yukicoder 2867
题目描述
求有多少个正整数 \(x\) 满足以下条件:
- \(x\le N\)。
- \(x\) 的十进制表示下不存在连续的 \(404\)。
思路
由于 \(N\) 非常大,所以考虑数位 dp。
令 \(dp_{i,0/1,0/1/2}\) 表示当前考虑到从高到低的第 \(i\) 位,是否有最高位限制,末尾存在 \(没有/4/40\) 的方案数。
按此状态转移即可。(转移太复杂了,此处省略)
时空复杂度均为 \(O(\log_{10} N)\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1000005, MOD = 998244353;
int n, f[MAXN][2][3], ans;
string s;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> s;
n = s.size(), s = ' ' + s;
f[0][1][0] = 1;
for(int i = 0; i < n; ++i) {
for(bool lim : {0, 1}) {
for(int op : {0, 1, 2}) {
int r = s[i + 1] - '0';
for(int num = 0; num <= (lim ? r : 9); ++num) {
if(!(op == 2 && num == 4)) {
f[i + 1][lim & (num == r)][(num == 4 ? 1 : (op == 1 && num == 0 ? 2 : 0))] = (f[i + 1][lim & (num == r)][(num == 4 ? 1 : (op == 1 && num == 0 ? 2 : 0))] + f[i][lim][op]) % MOD;
}
}
}
}
}
for(bool lim : {0, 1}) {
for(int op : {0, 1, 2}) {
ans = (ans + f[n][lim][op]) % MOD;
}
}
cout << (ans - 1 + MOD) % MOD;
return 0;
}
Yukicoder 2869
题目描述
你有 \(N\) 个背包,第 \(i\) 个背包的承重量为 \(e_i\)。你还有 \(M\) 个物品,第 \(i\) 个价值为 \(v_i\),重量为 \(w_i\)。
你可以往背包里放物品,使得这些物品的重量之和 \(\le e_i\)。
求最大的放入的物品价值之和,并给出方案。
思路
观察到 \(M\) 很小,所以考虑状压。
令 \(dp_{i,S}\) 表示已经装完了前 \(i\) 个背包,还剩物品集合 \(S\) 没有装。
我们可以预处理 \(V_S,W_S\) 表示物品集合 \(S\) 的价值之和,重量之和。
然后转移时就枚举 \(S\) 的子集 \(S'\),如果 \(W_{S'}\le e_{i+1}\),那么就可以装,否则不行。
由于我们每次刚好枚举 \(S\) 的子集,所以 dp 的时间复杂度为 \(O(N\sum \limits_{i=0}^M C_{M}^{i}\cdot 2^i)=O(N3^M)\)。
空间复杂度 \(O(N2^M)\),时间复杂度 \(O(N3^M)\)。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 17;
const ll INF = (ll)(1e18);
int n, m, e[MAXN], v[MAXN], w[MAXN], f[MAXN][1 << 16], ans, pos;
ll V[1 << 16], W[1 << 16], dp[MAXN][1 << 16];
void Print(int x, int y) {
if(!x) {
return;
}
Print(x - 1, y ^ f[x][y]);
cout << __builtin_popcount(f[x][y]) << " ";
for(int i = 1; i <= m; ++i) {
if((f[x][y] >> (i - 1)) & 1) {
cout << i << " ";
}
}
cout << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; ++i) {
cin >> e[i];
}
for(int i = 1; i <= m; ++i) {
cin >> v[i] >> w[i];
}
for(int i = 0; i < (1 << m); ++i) {
for(int j = 1; j <= m; ++j) {
V[i] += ((i >> (j - 1)) & 1) * v[j];
W[i] += ((i >> (j - 1)) & 1) * w[j];
}
}
for(int i = 0; i <= n; ++i) {
for(int j = 0; j < (1 << m); ++j) {
dp[i][j] = -INF;
}
}
dp[0][0] = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 0; j < (1 << m); ++j) {
for(int k = j; k >= 1; k = (k - 1) & j) {
if(W[k] <= e[i] && dp[i][j] < dp[i - 1][j ^ k] + V[k]) {
dp[i][j] = dp[i - 1][j ^ k] + V[k];
f[i][j] = k;
}
}
int k = 0;
if(W[k] <= e[i] && dp[i][j] < dp[i - 1][j ^ k] + V[k]) {
dp[i][j] = dp[i - 1][j ^ k] + V[k];
f[i][j] = k;
}
}
}
for(int i = 0; i < (1 << m); ++i) {
if(dp[n][i] > ans) {
ans = dp[n][i], pos = i;
}
}
cout << ans << "\n";
Print(n, pos);
return 0;
}