UVA 10910 Marks Distribution(组合数学 或 递推)

Marks Distribution

Time limit: 3.000 seconds

In an examination one student appeared in N subjects and has got total T marks. He has passed in all the Nsubjects where minimum mark for passing in each subject is P. You have to calculate the number of ways the student can get the marks. For example, if N=3T=34 and P=10 then the marks in the three subject could be as follows.

 

Subject 1

Subject 2

Subject 3

1

14

10

10

2

13

11

10

3

13

10

11

4

12

11

11

5

12

10

12

6

11

11

12

7

11

10

13

8

10

11

13

9

10

10

14

10

11

12

11

11

10

12

12

12

12

12

10

13

10

13

11

14

11

13

10

15

10

14

10

So there are 15 solutions. So F (3, 34, 10) = 15.

Input

In the first line of the input there will be a single positive integer K followed by K lines each containing a single test case. Each test case contains three positive integers denoting NT and P respectively. The values of NT and P will be at most 70. You may assume that the final answer will fit in a standard 32-bit integer.

Output

For each input, print in a line the value of F (N, T, P).

Sample Input

Output for Sample Input

2
3 34 10
3 34 10

15
15


题意:一个人N门课程的总成绩为T,每门课程的最低成绩为P,求一共有多少种可能的分配方法。

分析:

解法一:组合数学

这个问题可以转化为把m个物体放到n个盒子里面,允许有的盒子为空,问一共有多少种放置方法。因为除去每门课必须的P以后,剩下的M=T-N*P可以随意分配到N门课程里面。这样问题就变成了“插板法”的经典应用。分成N组需要N-1个隔板,再加上M个物体,可以看成N-1+M个空隙,然后从这些空隙中选出N-1用来放隔板,共有C(N-1+M,N-1)中种方法。

#include <cstdio>
typedef long long LL;

LL C(LL n, LL k) {
    if(n - k < k) k = n - k; //防止数据溢出
    LL ans = 1;
    for(int i = 1; i <= k; i++)
        ans = ans * (n - i + 1) / i;
    return ans;
}

int main() {
    int cas;
    LL n, t, p;
    scanf("%d", &cas);
    while(cas--) {
        scanf("%lld%lld%lld", &n, &t, &p);
        t = t - n * p;
        LL ans = C(n + t - 1, n - 1);
        printf("%lld\n", ans);
    }
    return 0;
}

解法二:递推

用a[i][j]表示前i个盒子里面放了j个物体的放法,则a[i][j] = a[i-1][0] + a[i-1][1] + …… + a[i-1][j].

初始化dp[i][0] = 1。

#include <cstdio>
#include <cstring>
const int N = 72;
typedef long long LL;
LL a[N][N];
int main() {
    int T;
    LL n, p, t;
    scanf("%d", &T);
    while(T--) {
        scanf("%lld%lld%lld", &n, &t, &p);
        t = t - n * p;
        memset(a, 0, sizeof(a));
        for(int i = 0; i <= n; i++)
            a[i][0] = 1;
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= t; j++) {
                for(int k = 0; k <= j; k++)
                    a[i][j] += a[i-1][k];
            }
        }
        printf("%lld\n", a[n][t]);
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值