SRM 475 DIV1 900

题意:

一场考试。一些题目结果已经出来,对于j题每个人能知道自己是否做对,做对得到points[j]分,做错0分。

令一些结果还未出来的题目,对于j题知道每个人是否提交,提交了就有机会得到points[j]分,否则必然的到0分。

然后对于成绩排在前p的人中(分数相同按照id,id小的排前面),随机选择q个,问方案数。


给这DP跪,完全没思路,然后看了一眼别人的dp状态,yy乱搞,结果就过了,现在整理一下思路。


先得到每个人最多能得的分数ma[i],和最少能得到的分数mi[i],对于我们选择的q个人,我们都假设他们得到最高分,其他都得他们的最低分,这样就不会出现中间情况,总方案数也不会减少。

枚举每一个1~n,假设他是选择的q个人中得分最低的。然后DP

假设现在选择x

状态dp[i][j][k]表示前i人,有j个排名在x前面,选择了k个的方案,转移看代码。

因为每次选择的方案最后一个人都不同,所以枚举x不会出现重复。


#include <cstdio>
#include <iostream>
#include <cstring>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <vector>
#define clr(x,y) memset(x,y,sizeof(x))
using namespace std ;
typedef long long ll ;
const double eps = 1e-6 ;

const int N = 55;
int mi[N],ma[N];
ll dp[N][N][N];
int n,p,q;

class RabbitProgramming
{
public:
    ll solve(){
        ll ans = 0;
        for(int x=1;x<=n;x++){
            clr(dp,0);
            dp[0][0][0] = 1;
            for(int i=1;i<=n;i++){
                if(i!=x){
                    for(int j=0;j<=n;j++){
                        for(int k=0;k<=n;k++){
                            if(i<x){  
                                //在x的前面的分数比ma[x]小于等于,排名就在他前面了,所以和i>x分开讨论
                                if(mi[i] < ma[x])
                                    dp[i][j][k] += dp[i-1][j][k]; 
                                        //i不加入比x大的行列
                                else
                                    if(j>=1) dp[i][j][k] += dp[i-1][j-1][k];
                                        //i必须加入比x大的行列
                                if(ma[i]>=ma[x])
                                    if(j>=1&&k>=1) dp[i][j][k] += dp[i-1][j-1][k-1];
                                        //i被选择
                            }
                            else{
                                if(mi[i]<=ma[x])
                                    dp[i][j][k] += dp[i-1][j][k];
                                else
                                    if(j>=1) dp[i][j][k] += dp[i-1][j-1][k];
                                if(ma[i]>ma[x])
                                    if(j>=1&&k>=1) dp[i][j][k] += dp[i-1][j-1][k-1];
                            }
                        }
                    }
                }
                else{
                    for(int j=1;j<=n;j++)
                        for(int k=1;k<=n;k++)
                            dp[i][j][k] = dp[i-1][j-1][k-1];
                    //x必须被加入
                }
            }
            for(int i=0;i<=p;i++)
                ans += dp[n][i][q];
            //有小于等于p个在它前面的方案都可以加入答案,而且不会出现重复,
            //因为对于固定了选择q个,有不同个排在前面的选择其中的方案都不会相同。
        }
        return ans;
    }
	long long getTeams(vector <int> p, vector <string> s, int qu, int se){
        ::n = s.size();
        ::p = qu;
        ::q = se;
        clr(mi,0);clr(ma,0);
        for(int i=0;i<(int)p.size();i++){
            if(p[i]>0){
                for(int j=0;j<n;j++)
                    if(s[j][i]=='Y')
                        mi[j+1]+=p[i],ma[j+1]+=p[i];
            }
            else{
                for(int j=0;j<n;j++)
                    if(s[j][i]=='Y')
                        ma[j+1] += -p[i];
            }
        }

		return solve();
	}



};


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值