Dollar Dayz
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 5651 | Accepted: 2124 |
Description
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$1Write 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,问总共有多少种方法。
//思路:
看大神的,这里贴上大神的思路:
可以转到a[i][j]的状态有两种,一种是a[i][j-1]就是不用j这个数字拼接i这个数字的方法数,另一种是a[i-j][j]就是用了j这个数字拼接的到i-j的方法数那么状态转移方程就可以写成a[i][j]=a[i][j-1]+a[i-j][j]不用加那么多项,就降低了一个数量级的复杂度,由于得到的结果可能相当大,已经超过了long long,所以应该用大数。但是若跑完所有数据,用大数会超过一秒,我们通过大数的程序可以达到,最大的数字为33位,那么,我们可以将两个long long的数字进行拼接,组成一个超过33位的数。这样增加了速度.
#include<stdio.h> #include<string.h> #include<math.h> #include<algorithm> #include<iostream> #define INF 1000000000000000000 #define ull unsigned long long #define ll long long #define IN __int64 #define N 1010 #define M 1000000007 using namespace std; ll a[N][N],b[N][N]; int main() { int t,n,m; int i,j,k; while(scanf("%d%d",&n,&k)!=EOF) { memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); for(i=0;i<=k;i++) a[0][i]=1; for(i=1;i<=k;i++) { for(j=1;j<=n;j++) { if(j-i<0) { b[j][i]=b[j][i-1]; a[j][i]=a[j][i-1]; continue; } b[j][i]=b[j][i-1]+b[j-i][i]+(a[j][i-1]+a[j-i][i])/INF; a[j][i]=(a[j][i-1]+a[j-i][i])%INF; } } if(b[n][k]) printf("%lld",b[n][k]); printf("%lld\n",a[n][k]); } return 0; }
//下面的是看到大神优化过的。
1、可以用完全背包解。
for(i=1...k)
for(j=i...n)
dp[j]+=dp[j-i];
由于1<=n<=1000,1<=k<=100,所以数据巨大,__int64 也表示不了。
故用两个long long 来表示。
2、可以用划分数的思想。
dp[n][m]表示把n分为若干份,且最大的那份<=m的方案数。
1、当m=1,n=1时,dp[n][m]=1
2、当m=n时,dp[n][m]=dp[n][n],dp[n][n]=dp[n][n-1]+1
3、当m>n时,dp[n][m]=dp[n][n]
4、m<n时,dp[n][m]=dp[n-m][m]+dp[n][m-1]< p="">
前者表示划分中有一个数为m,后者表示划分中所有的数都小于m,即所有的数都<=m-1。
dp[0][0...k]=1;0只有1种分法。
#include<stdio.h> #include<string.h> #include<math.h> #include<algorithm> #include<iostream> #define INF 1000000000000000000 #define ull unsigned long long #define ll long long #define IN __int64 #define N 1010 #define M 1000000007 using namespace std; ll a[N],b[N]; int main() { int n,k; while(scanf("%d%d",&n,&k)!=EOF) { memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); b[0]=1; for(int i=1;i<=k;i++) { for(int j=i;j<=n;j++) { a[j]=a[j]+a[j-i]+(b[j]+b[j-i])/INF; b[j]=(b[j]+b[j-i])%INF; } } if(a[n]) printf("%lld",a[n]); printf("%lld\n",b[n]); } return 0; }