1. 程式人生 > 其它 >【2021上海省賽】補倆題

【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;
}