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