YbtOJ NOIP2020 模拟赛 B 组 Day4 C. 模拟比赛【dp】


题目:

题目传送门


题意:

每个人的每题得分情况分为两种,要么能确定一定能得到或一定不能得到分数,要么就可能得到也可能得不到分数
问在分数前 s s s名中选出 t t t人的方案数有多少


分析:

转换一下题意,其实是在问有多少个大小为 t t t的集合能在 s s s当中
对于 ∀ x   ϵ   t \forall x\ \epsilon\ t x ϵ t,为了选到 t a ta ta,我们需要让 t a ta ta的分数最大化,而对于不在 t t t中的人的分数我们希望最小化,这样就是最有可能让 t   ϵ   s t\ \epsilon\ s t ϵ s
接下来我们就将分数的可能性转化为每人记录两个值:分数最大与最小值
f i , j , k f_{i,j,k} fi,j,k表示选到第 i i i人, t t t集合中已经有 j j j人, s s s集合中已经有 k k k
我们对于分数最大值进行降序排序,枚举一个 i j ij ij表示 t t t中已经有 i j ij ij
这样一来首先位置在 i j ij ij之后的数就没有考虑的必要了
位置在 i j ij ij之前的数,要选的话就考虑分数最大值,但因为我们是按照最大值降序排序的,所以直接做贡献即可;不选的话就考虑分数最小值,如果 t a ta ta的最小值还是大于 i j ij ij的最大值,就说明 t a ta ta一定是不在 t t t中却在 s s s的一个数
这样一直做到 i j − 1 ij-1 ij1,显然为了凑够 t t t个数,我们的答案需要统计 f i j − 1 , t − 1 , k f_{ij-1,t-1,k} fij1,t1,k,而 k k k的值是多少已经不重要了,因为我们关心的只是 t t t里的数,至于 s s s的数对当前集合的贡献不会造成任何影响,所以我们只需保证 k ⩽ s − t k\leqslant s-t kst,这是因为在集合中的 t t t个数也必定在 s s s当中


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
inline LL read()
{
    
    
	LL s=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {
    
    if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {
    
    s=s*10+c-'0';c=getchar();}
	return s*f;
}
LL xa[55],ia[55],a[55],p[55];
bool cmp(LL i,LL j) {
    
    return xa[i]>xa[j];}
LL f[55][55][55];
int main()
{
    
    
	freopen("ctsc.in","r",stdin);	
	freopen("ctsc.out","w",stdout);
	LL n=read();
	for(LL i=1;i<=n;i++) a[i]=read();
	LL m=read();char c[55];
	for(LL i=1;i<=m;i++) 
	{
    
    
	  p[i]=i;
	  scanf("%s",&c);
	  for(LL j=1;j<=n;j++) 
	  {
    
    
	  	if(a[j]>0) xa[i]+=a[j]*(c[j-1]=='Y'?1:0),ia[i]+=a[j]*(c[j-1]=='Y'?1:0);
	  	else xa[i]+=(-a[j])*(c[j-1]=='Y'?1:0);
	  }
	  xa[i]=xa[i]*100+m-i;
	  ia[i]=ia[i]*100+m-i;
	}
	sort(p+1,p+1+m,cmp);
	LL s=read(),t=read(),ans=0;
	for(LL i=1;i<=m;i++)
	{
    
    
		LL lim=s-t+1;
		memset(f,0,sizeof(f));
		f[0][0][0]=1;
		for(LL j=1;j<i;j++)
		  for(LL c=0;c<t;c++)
		    for(LL b=0;b<lim;b++)
		    {
    
    
		    	if(b&&ia[p[j]]>xa[p[i]]) f[j][c][b]+=f[j-1][c][b-1];
		    	else if(ia[p[j]]<xa[p[i]]) f[j][c][b]+=f[j-1][c][b];
		    	if(c) f[j][c][b]+=f[j-1][c-1][b]; 
			}
		for(LL b=0;b<lim;b++) ans+=f[i-1][t-1][b];
	}
	cout<<ans;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35786326/article/details/109035511