题目大意:提供面额1~300的硬币(美元硬币这题性质一样),题目有三种输入,分别是:
1,输入N——输出1~300硬币组成N的方案数;
2,输入N,L1——输出组成N的方案数中,硬币数不超过L1的方案数;
3,输入N,L1,L2——输出组成N的方案数中, L1 <= 硬币数 <= L2的方案数;
解题策略:重要结论,组合N硬币数不超过L1的方案数 = 组合N硬币面额最大不超过L1的方案数;
上述结论可通过Ferrers图证明(这货是组合数学里的东西,小菜不懂啊。。);
所以通过该结论,这题就可以uva 674 Coin Change联系起来。
由于是dp基础题,所以就不献丑谈算法了,说下注意点和不同点吧:
1,解决输入问题,网上都是用sscanf()函数,由于不熟悉,就用字符流解决了;
2,在判断上下限时,必须考虑上限超过300这种坑爹数据(用min函数解决);
3,当价格为0而且下限为0时,只要一种组合方案;
算法的晦涩复杂和美妙无比都在一瞬之间啊,最后谢谢周B捷童鞋~
/*
UVA 10313 Pay the Price
AC by J.Dark
ON 2013/3/15
Time 0.404s
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <sstream>
#include <algorithm>
const int maxn = 310;
using namespace std;
unsigned long long int dp[maxn][maxn];
int N, L1, L2;
int price, minNum, maxNum;
char temp[100];
void solve(){
memset(dp, 0, sizeof(dp));
dp[0][0]=1;
for(int i=0; i<=300; i++) {dp[0][i] = 1;}
for(int j=1; j<=300; j++){
for(int i=1; i<=300; i++){
dp[i][j] = dp[i][j-1];
if(i-j >= 0) {dp[i][j] += dp[i-j][j];}
}
}
}
int main(){
solve();
while(gets(temp)!=NULL)
{
int num, tc = 0;
//定义字符流,分割数字
stringstream ss(temp);
while(ss >> num && tc <=3)
{
tc++;
if(tc == 1) price = num;
if(tc == 2) L1 = num;
if(tc == 3) L2 = num;
}
//根据数字情况判断硬币数上下限
if(tc == 1) {maxNum = 300; minNum = 0;}
if(tc == 2) {maxNum = min(300, L1); minNum = 0;}
if(tc == 3 ) {minNum = L1, maxNum = min(300, L2);}
//特判
if(price == 0 && minNum == 0) {cout << 1 << endl;}
else {cout << dp[price][maxNum]-dp[price][minNum-1]<< endl;}
}
//system("pause");
return 0;
}