【补题】牛客周赛 Round 31

文章介绍了两道编程竞赛题目:如何用线性动态规划求解E题中小红子集取反的最少操作次数,以及F题中计算连续段数量的数学方法。涉及转移方程、dp数组优化和数学公式应用。
摘要由CSDN通过智能技术生成

链接:https://ac.nowcoder.com/acm/contest/74362

E-小红的子集取反

思路

线性dp,定义 d p i , j dp_{i,j} dpi,j表示使得数组前 i i i项元素和为 j j j的最小取反次数,初始状态 d p 0 , 0 = 0 dp_{0,0}=0 dp0,0=0,转移方程:
d p i , j = m i n ( d p i − 1 , j + a i , d p i − 1 , j + ( − a i ) + 1 ) dp_{i,j}=min(dp_{i-1,j+a_i},dp_{i-1,j+(-a_i)}+1) dpi,j=min(dpi1,j+ai,dpi1,j+(ai)+1)
注意 a i a_i ai的和可能为负数,所以需要在 d p dp dp数组中使用偏移量,这里偏移量取 40000 40000 40000

代码

#include <bits/stdc++.h>

using namespace std;

int n, a;

int main() {
  ios::sync_with_stdio(false);
  cin >> n;
  vector<vector<int>> dp(n + 1, vector<int>(8e4 + 1, n + 1));
  int maxs = 40000;
  dp[0][0 + maxs] = 0;
  for (int i = 1; i <= n; i++) {
    cin >> a;
    for (int j = -maxs; j <= maxs; j++) {
      if (-maxs <= j + a && j + a <= maxs) {
        dp[i][j + maxs] = min(dp[i][j + maxs], dp[i - 1][j + a + maxs]);
      }
      if (-maxs <= j - a && j - a <= maxs) {
        dp[i][j + maxs] = min(dp[i][j + maxs], dp[i - 1][j - a + maxs] + 1);
      }
    }
  }
  if (dp[n][0 + maxs] == n + 1) {
    cout << "-1\n";
  } else {
    cout << dp[n][0 + maxs] << '\n';
  }
  return 0;
}

F-小红的连续段

思路

数学题,对于连续段数量恰好为 i i i的字符串,有两种情况:

  1. 字符’a’的连续串有 ⌊ i 2 ⌋ \lfloor\frac{i}{2}\rfloor 2i个,字符’b’的连续串有 ⌊ i + 1 2 ⌋ \lfloor\frac{i+1}{2}\rfloor 2i+1
  2. 字符’a’的连续串有 ⌊ i + 1 2 ⌋ \lfloor\frac{i+1}{2}\rfloor 2i+1个,字符’b’的连续串有 ⌊ i 2 ⌋ \lfloor\frac{i}{2}\rfloor 2i

记字符’a’的连续串数量为 a a a,字符’b’的连续串数量为 b b b,则根据隔板法和乘法原理,有 ( x − 1 a − 1 ) ( y − 1 b − 1 ) \dbinom{x-1}{a-1}\dbinom{y-1}{b-1} (a1x1)(b1y1)种情况。

代码

#include <bits/stdc++.h>

using namespace std;

const int MOD = 1e9 + 7;

int x, y;
long long f[1005], invf[1005];

long long qpow(long long a, int b) {
  long long ans = 1;
  while (b) {
    if (b & 1) {
      ans = ans * a % MOD;
    }
    a = a * a % MOD;
    b >>= 1;
  }
  return ans;
}

long long C(int a, int b) { return f[a] * invf[b] % MOD * invf[a - b] % MOD; }

int main() {
  ios::sync_with_stdio(false);
  cin >> x >> y;
  f[0] = f[1] = invf[0] = invf[1] = 1;
  for (int i = 2; i <= 1000; i++) {
    f[i] = f[i - 1] * i % MOD;
    invf[i] = qpow(f[i], MOD - 2);
  }
  for (int i = 1; i <= x + y; i++) {
    long long ans = 0;
    int a = i / 2, b = (i + 1) / 2;
    if (1 <= a && a <= x && 1 <= b && b <= y) {
      ans = (ans + C(x - 1, a - 1) * C(y - 1, b - 1) % MOD) % MOD;
    }
    a = (i + 1) / 2, b = i / 2;
    if (1 <= a && a <= x && 1 <= b && b <= y) {
      ans = (ans + C(x - 1, a - 1) * C(y - 1, b - 1) % MOD) % MOD;
    }
    cout << ans << '\n';
  }
  return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值