UVa 10313 - Pay the Price

传送门

UVa 10313 - Pay the Price

题意

输入的第一个数字是需要换钱的数额。

如果只有第一个数,输出总共的方式。
如果有第二个数,输出用不超过第二个数的硬币换钱的方式。
如果有第三个数,输出用多于第二个不超过第三个的硬币换钱的方式。

思路

参考了Staginner和ACEndless的解题报告。
引用一下他的说明

这个题目涉及到一个结论,用不超过j个硬币凑出面值i的方案种数,是和用面值不超过j的硬币凑出面值i的方案种数是相同的。说得再数学一点,就是整数i拆分成不超过j个整数的拆分数,是和整数i拆成若干个值不超过j的整数的拆分数是相同的。具体的证明用到了Ferrers图像的性质。

这样的话我们就可以取一个二维数组$f[i][j]$表示用面值不超过j的硬币凑出面值i的方案的种数,那么如果我使用了面值j,对应方案种数就应该加上$f[i-j][j]$,如果我们不使用面值j,那么对应的方案种数就应该加上$f[i][j-1]$。也就是说状态转移方程为$f[i][j]= f[i-j][j]+ f[i][j-1]$。

代码

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 301;
const int INF = 0x3f3f3f3f;
#define LL long long
 
LL dp[MAXN][MAXN];

int main()
{
   // freopen("input.txt", "r", stdin);
    int n, i, j, st, ed, temp money;
    char str[20];
    dp[0][0] = 1;
    for (i = 0; i < MAXN; i++)
        for (j = 1; j < MAXN; j++)
        {
            if (j <= i)
                dp[i][j] = dp[i - j][j] + dp[i][j - 1];
            else
                dp[i][j] = dp[i][j - 1];
        }
    while (gets(str))
    {
        st = ed = -1;
        sscanf(str, "%d%d%d", &money, &st, &ed);
        st = min(st, 300), ed = min(ed, 300);
        if (st == -1)
            printf("%lld\n", dp[money][money]);
        else if (ed == -1)
            printf("%lld\n", dp[money][st]);
        else
        {
            if (st < 2)
                printf("%lld\n", dp[money][ed]);
            else
                printf("%lld\n", dp[money][ed] - dp[money][st - 1]);
        }
    }
    return 0;
}

Powered by Jekyll and Theme by solid