Uva674 完全背包求方案数
记忆化搜索。注意输入n的位置,否则Tle。
dp[i][j]表示用前j种硬币组成i分钱时的种类数
那么状态转移方程是:dp[i][j]+=DP(i-k*v[j],j-1)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; #define ll long long int dp[8000][5]; int q[5]= {1,5,10,25,50}; int solve(int s,int k) { if(dp[s][k]!=-1) return dp[s][k]; dp[s][k]=0; for(int i=k; i<5 && s>=q[i]; i++) dp[s][k]+=solve(s-q[i],i); return dp[s][k]; } int main() { memset(dp,-1,sizeof(dp)); for(int i=0; i<5; i++) dp[0][i]=1; int n; while(cin>>n) cout<<solve(n,0)<<endl; return 0; }
递推的写法,自小向大推:
#include<iostream> using namespace std; int n,coin[5]= {1,5,10,25,50}; long long dp[8000]= {1}; int main() { for(int i=0; i<5; i++) for(int j=0; j<7500; j++) dp[j+coin[i]]+=dp[j]; while(cin>>n) cout<<dp[n]<<endl; return 0; }
另一种:
#include<stdio.h> #include<string.h> #define N 7500 int dp[N][6]; int v[5]= {1,5,10,25,50}; int DP(int i,int j) { if(dp[i][j]!=-1) return dp[i][j]; dp[i][j]=0; for(int k=0; i-k*v[j]>=0; k++) dp[i][j]+=DP(i-k*v[j],j-1); return dp[i][j]; } int main() { int n; memset(dp,-1,sizeof(dp)); while(scanf("%d",&n)!=EOF) { for(int i=0; i<=n; i++) dp[i][0]=1; printf("%d\n",DP(n,4)); } return 0; }