Dollar Dayz poj 3181(dp 完全背包 或 不超过m的划分数)

本文介绍了一个经典的组合问题:如何计算在有限预算内,购买不同价格工具的所有可能组合数量。通过两种不同的方法——递推和完全背包问题的解决思路,详细阐述了算法实现,并提供了完整的代码示例。

Farmer John goes to Dollar Days at The Cow Store and discovers an unlimited number of tools on sale. During his first visit, the tools are selling variously for $1, $2, and $3. Farmer John has exactly $5 to spend. He can buy 5 tools at $1 each or 1 tool at $3 and an additional 1 tool at $2. Of course, there are other combinations for a total of 5 different ways FJ can spend all his money on tools. Here they are: 

        1 @ US$3 + 1 @ US$2

        1 @ US$3 + 2 @ US$1

        1 @ US$2 + 3 @ US$1

        2 @ US$2 + 1 @ US$1

        5 @ US$1
Write a program than will compute the number of ways FJ can spend N dollars (1 <= N <= 1000) at The Cow Store for tools on sale with a cost of $1..$K (1 <= K <= 100).
Input
A single line with two space-separated integers: N and K.
Output
A single line with a single integer that is the number of unique ways FJ can spend his money.
Sample Input
5 3
Sample Output
5

题意:输入:n k,用从1~k之间的整数,相加能得出来n,不同式子表示方法有多少种;

第一种理解方式: 最大不超过k的 n划分数 有多少种;

dp[n][k] 为 最大不超的k的 n的划分数的种数;

dp[n][k] = dp[n][k-1] + dp[n-k][k];

分含k和不含k的式子:

dp[n][k-1] 为不包含的k的划分数;

dp[n-k][k]  为包含k的划分数,让n-k的每个划分数后都加个k;


一定要注意初始化时,dp[0][k] ,不超过k的整数,和为0 都赋予 1;第二个代码有解释:

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max  1010

long long  dp[Max][110],dp1[Max][110];    // dp[i][j]  不超过j 的数组成i的划分数;
//dp 存一个数的后十七位,dp1 存一个数后十七位之前的位数; 
int main()
{
	int i,j;
	int n,k;
	long long inf = 1;
	for(i = 0;i<18;i++)
		inf *= 10;
	while(~scanf("%d%d",&n,&k))
	{
		memset(dp1,0,sizeof(dp1));
		for(i = 0;i<=k;i++)
		{
			dp[0][i] = 1;   // 组成0的划分数永远都为1; 
 		}
		for(i = 1;i<=n;i++)
		{
			for(j = 1;j<=k;j++)
			{
				if(i<j)
				{
					dp[i][j] = dp[i][j-1];
					dp1[i][j] = dp1[i][j-1];
				}
				else
				{
					dp1[i][j] = dp1[i][j-1]+dp1[i-j][j]+(dp[i][j-1]+dp[i-j][j])/inf;
					dp[i][j] = (dp[i][j-1]+dp[i-j][j])%inf;
				}
				 //dp[i][j-1];   不包含 j的划分数 
				 //dp[i-j][j];  包含j 的划分数; 
			}
		}
		
		if(dp1[n][k])
			printf("%lld%017lld\n",dp1[n][k],dp[n][k]);
		else printf("%lld\n",dp[n][k]);
	}
	return 0;
} 

思路二:

完全背包;

把 k种玩具的价格当做是 有多少种物品, n 为 背包容量;

dp[i][j]  前i种玩具能组成 j 的 划分数;

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
__int64 dp[1110][1110],dp1[1110][1110];  //dp[i][j] 表示为从前i中的玩具中的组成 j 元的方法数;
// dp为后17位,dp1为后17为之前的数; 
int main()
{
	int n,k,i,j;
	while(~scanf("%d%d",&n,&k))
	{
		memset(dp1,0,sizeof(dp1));
		for(i = 0;i<=n;i++)
		{						// 初始化很重要 
			dp[i][0] = 1;     // 前i中玩具组成 0 元的全部为 1;为0元素; 
		}					 // dp[i][j],当i==j时,dp[i][j] +=dp[i][j-i] 
							//  0 元素被计数时,就变成了0+j,被计数了;
							//所以前i中玩具组成 0 元的全部为 1; 
		__int64 inf = 1;
		for(i = 0;i<18;i++)
		{
			inf *= 10;
		} 
		/*for(i = 1;i<=k;i++)
		{
			for(j = 1;j<=n;j++)
			{
				//dp[i][j] = 0;
				dp[i][j] = dp[i-1][j];
				for(int t = 1;t*i<=j;t++)     //真正的完全背包是这样的,这是耗时的; 
				{
					if(j>=t*i);
						dp[i][j] += dp[i-1][j-t*i];	
				}
			}
		}*/
		for(i = 1;i<=k;i++)
		{
			for(j = 1;j<=n;j++)
			{
				if(j<i)
				{
					dp[i][j] = dp[i-1][j];
					dp1[i][j] = dp1[i-1][j];   // 17之前的数,17位之后的数; 
				}
				else 
				{
					dp1[i][j] = (dp1[i-1][j]+dp1[i][j-i])+(dp[i-1][j]+dp[i][j-i])/inf;
					dp[i][j] = (dp[i-1][j] + dp[i][j-i])%inf;  //dp[i][j-i] 前面所有的情况已经加过了;直接加前面总体的结果就行了;
				}	 	 
			}
		}
		if(dp1[k][n]!=0)
			printf("%I64d%017I64d\n",dp1[k][n],dp[k][n]);
		else printf("%I64d\n",dp[k][n]);
	}
	return 0;
} 


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值