题目大意:有五个数,分别为:1 , 5 , 10 , 25 , 50 ,对于任意输入的一个正整数n , n由其中的一个或者多个数组合累加得到,求有多少种方法。例如:11可以由11个1组成,也可以由6个1一个5组成,还可以有一个10 ,一个1组成,或者2个5和一个1,一共有4种方法.
开始的时候想用一个一维数组来表示,对应的数n的可能组成方式个数,状态转移的时候发现有大量的重复,这样求出的结果中有很多的重复。开一个二维数组来表示,横向表示,用前j个数,可能的组成方式个数,这样状态转移的时候就不会出现重复了。
设num[i][j] 表示整数i有前j个数的组成的所有可能情况。
num[i][j] = num[ i - data [ j ] ] [ j ] ( i / data [ j ] > 0 ) && num [ i ] [ j ] += num[ i ] [ j - 1 ] ( j > 0 )
源代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxn = 7500 ;
int num[maxn][6];
int data[] ={1 , 5 , 10 , 25 , 50} ;
int n ;
int main()
{
// freopen("in.txt" , "r" , stdin);
int i ;
int j ;
memset(num , 0 , sizeof(num));
num[0][0] = 1 ;
for(i = 0 ; i <= 7500 ; i ++)
{
for(j = 0 ; j < 5 ; j ++)
{
if(i/data[j])
{
num[i][j] = num[i-data[j]][j] ;
}
if(j > 0)
num[i][j] += num[i][j-1] ;
}
}
while(cin>>n)
{
printf("%d\n" , num[n][4]) ;
}
return 0;
}
补充 ,在用递推的方式AC后, 又用记忆化搜索的方式试了下,代码如下:
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std ;
const int maxn = 7500 ;
int data[] = { 1 , 5 , 10 , 25 , 50} ;
int num[maxn][5] ;
int dp(int , int ) ;
int n ;
int main()
{
//freopen("in.txt" , "r" , stdin) ;
memset(num , 0 , sizeof(num)) ;
while(cin>>n)
{
cout<<dp(4 , n)<<endl ;
}
return 0 ;
}
int dp(int layer , int s)
{
if( s == 0)
return 1 ;
int & ans = num[s][layer] ;
if(ans > 0)
return ans ;
if(s >= data[layer])
ans += dp(layer , s - data[layer]) ;
if(layer > 0)
ans += dp(layer - 1 , s ) ;
return ans ;
}