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;
}
posted @ 2024-09-26 10:11  Yaosicheng124  阅读(2)  评论(0编辑  收藏  举报