AtCoder Beginner Contest 238

F - Two Exams

题目概述:
n n n 个公民,现在对这 n n n 个公民分别进行了两次排名,每个城市获得的排名分别为 P i P_i Pi Q i Q_i Qi,现在要在这 n n n 个公民中选出 K K K 个城市,并且给出以下约束:

  • 当选择 x x x 而不选择城市 y y y 时,一定不能有 P x > P y P_x > P_y Px>Py Q x > Q y Q_x > Q_y Qx>Qy

思路:
上述条件是说,如果市民 X X X 的两次排名都比市民 Y Y Y 要靠前,那么我们选择 Y Y Y 之前必须把 X X X 选上。
在上述条件中,对于每个市民我们都只有选或不选两个选项,可以看出这是一个有约束的 01 01 01 背包问题,我们现在可以表示的状态有:枚举到了第 i i i 个市民,在前 i i i 个市民中我们一共选择了 j j j 个。
对于每个市民选或者不选的约束,首先,我们将这些市民以 P i P_i Pi 升序排序,这样我们正向枚举时,保证了 P i P_i Pi 是递增的。
然后我们可以单独开一维数组,来记录当前已经选择了的市民中, 最低的 Q i Q_i Qi 是多少。
最终的状态表示: f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] i i i 个人中,选择了 j j j 个人,并且当前选择的人中排名最靠后的人为 k k k
假设当前已经选择的人中 Q i Q_i Qi 最靠后的排名为 k k k ,只有当枚举到的 Q i < k Q_i < k Qi<k 时,才可以选择第 i i i 个人:
f[i][j + 1][k] += f[i - 1][j][k]
同时也可以不选择第 i i i 个人:
f[i][j][k] += f[i - 1][j][k]
但是,如果此时的 Q i < k Q_i < k Qi<k ,此时应当更新 我们不能够跳过 Q i Q_i Qi 去选择 第 k k k大的值,所以将当前的最靠后排名 k k k换为 Q i Q_i Qi 即可.
f[i][j][Q_i] += f[i - 1][j][k]

初始化:
全选只有一种方案: f[n][n][n] = 1
一个不选也只有一种方案 f[0][0][n + 1] = 1
代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> PII;
const int N = 310, mod = 998244353;
int f[N][N][N];

int main() {
   ios::sync_with_stdio(false); 
   cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
   freopen("D:/Cpp/program/Test.in", "r", stdin);
   freopen("D:/Cpp/program/Test.out", "w", stdout);
#endif
    int n, K;
    cin >> n >> K;
    PII a[n + 1];
    for(int i = 1; i <= n; i ++) cin >> a[i].first;
    for(int i = 1; i <= n; i ++) cin >> a[i].second;

    sort(a + 1, a + n + 1);
    f[0][0][n + 1] = 1;
    f[n][n][n] = 1;
    for(int i = 1; i <= n; i ++) {
        for(int j = 0; j <= K; j ++) {
            for(int k = 0; k <= n + 1; k ++) {
                //如果不选择 a[i] 那么a[i].second 一定是大于 k 的, 
                //如果 小于 k 则需要将结尾为 k 的数换为 a[i].second
                f[i][j][min(a[i].second, k)] += f[i - 1][j][k];
                f[i][j][min(a[i].second, k)] %= mod;

                if(a[i].second < k) {
                    f[i][j + 1][k] += f[i - 1][j][k];
                    f[i][j + 1][k] %= mod;
                }
            }
        }
    }

    int ans = 0;
    //如果 K == n 即选择所有人 
    for(int i = 1; i <= n; i ++) {
        // cout << f[n][K][i] << '\n';
        ans += f[n][K][i];
        ans %= mod;
    }
    // if(K == n) cout << 1 << '\n';
    // else 
    cout << ans << '\n';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值