hdu2079选课时间(动态规划&&母函数)

23 篇文章 0 订阅
8 篇文章 0 订阅

选课时间(题目已修改,注意读题)

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门。
 

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bokzmm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值