【2021上海省赛】补俩题

D. Zztrans 的班级合照

思路:

  • 容易发现,同学们从从低到高来站队,每次必须保证已经站在第一排的人数大于等于第二排的人数。
  • \(dp[i][j]\)表示已经占了i个人,有j个人站在第前排的概率。
  • 为了避免重复计算,可以把相同的缩成一个点,最后的答案再乘以相同的个数的全排列。
  • 还要枚举相同的几个数放在前一排了多少个。
点击查看代码
int a[N]; ll dp[5005][3000]; ll F[5005]; int book [N];
vector<int>ve;
void init(){
    F[0] = 1;
    for(int i=1;i<=N-5;i++) F[i] = F[i-1]*i%mod;
}
int main(){
    int n;
    cin>>n;
    init();
    for(int i = 1;i <= n; i ++) cin >> a[i],book[a[i]]++;
    for(int i = 1; i <= n;i++)if(book[i]) ve.push_back(book[i]);
    dp[0][0] = 1;
    int pre = 0;
    for(auto i:ve){
        pre += i;
        for(int j = min(pre,n/2) ;j>=pre - j ; --j){//注意转移顺序
            for(int k = 0; k<=i&&k<=j;k++){
                dp[pre][j] = (dp[pre][j] + dp[pre-i][j-k])%mod;
            }
        }
    }
    ll ans = dp[n][n/2];
    for(int i = 1; i<=n ;++i) ans =ans*F[book[i]]%mod;
    cout<<ans<<endl;
    return 0;
}

B. 小 A 的卡牌游戏

思路:

点击查看代码
struct node{
    int a,b,c;
    bool operator < (const node &B)const{
        return a-b>B.a-B.b;
    }
}A[N];
ll f[5005][5005];
int main(){
    int n,a,b,c;
    cin>>n>>a>>b>>c;
    for(int i=1;i<=n;i++)cin>>A[i].a>>A[i].b>>A[i].c;
    sort(A+1,A+n+1);
    for(int i=0;i<=n;i++){
        for(int j=0;j<=n;j++){
            f[i][j] = -1e18;
        }
    }
    f[0][0] = 0;
    for(int i=1;i<=n;i++){
        for(int j = 0;j <= i;j++){
            if(j) f[i][j] = f[i-1][j-1] + A[i].c;
            int res = i - j;
            if(res <= a) f[i][j] = max(f[i][j],f[i-1][j]+A[i].a);
            else f[i][j] = max(f[i][j],f[i-1][j]+A[i].b);
        }
    }
    cout<<f[n][c]<<endl;
    return 0;
}
posted @ 2021-09-17 12:05  qingyanng  阅读(62)  评论(1编辑  收藏  举报