题目
题目大意
小A在玩游戏,有n, a, b, c四个数。他要进行若干次操作。每次操作分为两种:1.n - a 2.n - b 当n < c时,终止操作。请问共有几种不同的操作方法(当两次操作的操作次数不同,或操作次 数相同时,某一时刻的操作不同,则认为这是两种不同的操作方式。121与112是两种操作方法)
输入一
1 1 1 1
输出一
1
输入二
114 51 4 1
输出二
176
数据范围
1 <= a,b,c <= n
对于20%的数据 a=b=c=1, n <= 30
对于40%的数据 c=1 a <= 1000
对于所有数据 1<=a, b, c, n<=2e5
分析
在看到题目时,第一时间想到的是枚举1类操作和2类操作的数量,用乘法原理加快速幂算。写完之后发现答案始终少一点。在草稿纸上写写画画后,发现了问题。以输入二为例:
4 4 4 ...... 4 51(28个4,1个51)是可行的方案,但是在枚举过程中枚举不出。原因就在于我们直接确定了操作的数量,直接按照正正好取到n的情况计算得出答案,算不到上述情况。
突然想到我们可以反向枚举。类似于递推,从n=1推起,一直退到n=n,很像斐波那契数列,初始值为1~min(a, b),按照斐波那契数列的做法 f[i] = f[max(0, i-a)] + f[max(0, i-b)] 为递推式,就可以求啦。
解答
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, a, b, c, t[N];
int main() {
cin >> n >> a >> b >> c;
if (a > b) swap(a, b); //习惯把大的放前面,不放也可以
if (n == c) { //特判n=c时无法操作,也就是只有一种操作
cout << "1" << endl;
return 0;
}
for (int i = 0; i <= c; ++ i)
t[i] = 1;
//初始化
for (int i = c + 1; i <= n; ++ i) {
int u = i - a;
int v = i - b;
if (u < 0) u = 0; //以防数组下标越界
if (v < 0) v = 0; //同上
t[i] = t[v] + t[u];
}
cout << t[n] << endl;
return 0;
}