选课时间(题目已修改,注意读题)
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3513 Accepted Submission(s): 2772
Problem Description
又到了选课的时间了,xhd看着选课表发呆,为了想让下一学期好过点,他想知道学n个学分共有多少组合。你来帮帮他吧。(xhd认为一样学分的课没区别)
Input
输入数据的第一行是一个数据T,表示有T组数据。
每组数据的第一行是两个整数n(1 <= n <= 40),k(1 <= k <= 8)。
接着有k行,每行有两个整数a(1 <= a <= 8),b(1 <= b <= 10),表示学分为a的课有b门。
每组数据的第一行是两个整数n(1 <= n <= 40),k(1 <= k <= 8)。
接着有k行,每行有两个整数a(1 <= a <= 8),b(1 <= b <= 10),表示学分为a的课有b门。
Output
对于每组输入数据,输出一个整数,表示学n个学分的组合数。
Sample Input
2 2 2 1 2 2 1 40 8 1 1 2 2 3 2 4 2 5 8 6 9 7 6 8 8
Sample Output
2 445题目大意:给出要修够的学分及可以选修的课,问有多少种方案可以修够这些学分?解题思路:本题的解法大致有两种,一个是动态规划,一个是母函数,感觉组合问题大多都能用动态规划和母函数解决。其中动态规划的解法为:已知 需修够学分 N,各学分s[i],及其对应的 课的数num[i].若用j表示需修够学分,则修够学分为j的方案数就是已经找到的方案数dp[j]和当已修l*s[i]学分时,要修够j学分的方案数即dp[j-l*s[i]]。则可得动态转移方程为:dp[j] = dp[j] + dp[j-l*s[i]] (j = n,n-1,...1; l = 1,2,3,...num[i]).ac代码:#include<stdio.h> #include<string.h> int dp[45]; int main() { int t; int n,k,i,j,l; int s[10],num[10]; scanf("%d",&t); while(t- -) { scanf("%d%d",&n,&k); for(i=0;i<k;i++) scanf("%d%d",&s[i],&num[i]); memset(dp,0,sizeof(dp)); dp[0]=1; for(i=0;i<k;i++) { for(j=n;j>=s[i];j--) { for(l=1;(j-l*s[i])>=0&&l<=num[i];l++) { dp[j]=dp[j]+dp[j-l*s[i]]; } } } printf("%d\n",dp[n]); } return 0; }
母函数的解法为:
已知母函数的通式为 G(x) = 1 + a[1]x + a[2]x^2 + a[3]x^3 +.........+ a[n]x^n其中,每一项x的系数a[i]就可以表示幂数i的划分的方案数。应用到本题中可知:学分为1的可以表示的学分为:1 + x + x^2 + x^3 + x^4 +....学分为2的可以表示的学分为:1 + x^2 + x^4 + x^6 +........学分为3的可以表示的学分为:1 + x^3 + x^6 + x^9+.........以此类推用所给的各学分所能表示的多项式相乘,化简后得到a[n]x^n,a[n]就是要修够学分n的方案数。代码如下;#include<stdio.h> #include<string.h> int c1[1010],c2[1010]; int main() { int t,n,k; int i,j,l,num; int a[10],b[10]; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&k); num=0; for(i=0;i<k;i++) { scanf("%d%d",&a[i],&b[i]); } memset(c2,0,sizeof(c2)); for(i=1;i<=k;i++) { for(j=0;j<=n;j++) { for(l=0;l+j<=n;l+=a[i]) { c2[l+j]+=c1[j]; } } for(j=0;j<=n;j++) { c1[j]=c2[j],c2[j]=0; } } printf("%d\n",c1[n]); } return 0; }