题意:
给出 \(N\) , \(l_1\) , \(l_2\) , \(N\) 表示钱的面值,问将N拆分可以有多少种拆分数。
有三种情况:
1,给出 \(N\),将 \(N\) 拆分成面值不超过 \(N\) 的硬币可以有多少种不同的拆分数。
2,给出 \(N\) 和 \(l_1\) ,将 \(N\) 拆分成面值不超过 \(l_1\) 的硬币可以有多少种不同的拆分数。
3,给出 \(N\) 和 \(l_1\) 和 \(l_2\),将 \(N\) 拆分成面值在 \(l_1\) 到 \(l_2\) 范围内的硬币可以有多少种不同的拆分数。
思路:动态规划的思路还是比较明显,但不太好想的就是转移方程,如果用 \(dp[ i ][ j ]\) 表示将面值为 \(i\) 拆分成面值不超过 \(j\) 的硬币可以有多少种拆分。
那么可以用 \(dp[ i - j ][ j ]\) 表示选择面值为 \(j\) 的硬币,\(dp[ i ][ j - 1 ]\) 表示不选择面值为j的硬币。
那么状态转移方程就是: \(dp[ i ][ j ] += dp[ i - j ][ j ] + dp[ i ][ j - 1 ]\)
由于数据给的 \(n<=300\) 那么可以考虑在读入前进行预处理,读入时就可以进行 \(O(1)\) 的查询。
另外,本题的输入比较毒瘤,需要注意
#include<stdio.h>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
int n;const int MAXN = 305;
ll dp[MAXN][MAXN];
void deal(){
dp[0][0]=1;
for(int i=0;i<=300;i++){
for(int j=1;j<=300;j++){
if(i-j >= 0)dp[i][j] += dp[i - j][j];
dp[i][j] += dp[i][j - 1];
}
}
}
int main(){
deal();
while(~scanf("%d",&n)){
int a=-1,b=-1;char f = getchar();int ID=0;
while(f != '\n'){
if(isdigit(f)){
if(ID==2){if(b==-1) b=0;b = b*10+f-'0';}
if(ID==1){if(a==-1) a=0;a = a*10+f-'0';}
}
if(f == ' ') ID++;
f=getchar();
}
if(a > 300) a = 300;
if(b > 300) b = 300;
if(a != -1 && b == -1){
if(a > 300) a =300;
printf("%lld\n",dp[n][a]);
}
else if(a == -1 && b == -1){
printf("%lld\n",dp[n][n]);
}
else if(a!=-1 && b!=-1){
if(a >= 1) printf("%lld\n",dp[n][b] - dp[n][a-1]);
else printf("%lld\n",dp[n][b]);
}
}
return 0;
}