1664 | Accepted | 220K | 0MS | C++ | 1766B |
1664 | Accepted | 232K | 16MS | C++ | 1175B |
DP的做法是看discuss中有人提到的,非常巧妙。即对于任意的M和N,有两种情况:1.第一个盘子放一个苹果,那么N个盘子都得放一个苹果,即等同于DP[M - N][N];2.如果第一个盘子不放苹果,那么只用考虑后面 N-1个盘子就好,即等同于DP[M][N-1]。所以,我们有递推表达式:DP[M][N] = DP[M - N][N] + DP[M][N - 1].
DFS的做法是用3个量表示一个状态:m:当前要放的苹果的数目;last:上次(即后面那个盘子)放的苹果数目,当前要放的苹果数目不能大于last,另外当前要放的苹果数目不能少于m/n,这是因为要保证如果前面的所有盘子都和当前放的一样多时能够把m个苹果都放下了;n:当前正在放的盘子编号,这里用的是DFS从后往前放,即从盘子的编号N到1。这3个量可以唯一地确定一个状态,我用了一个三维数组cache[11][11][11]保存结果,避免重复运算。见main1()。
/* ID: thestor1 LANG: C++ TASK: poj1664 */ #include <iostream> #include <fstream> #include <cmath> #include <cstdio> #include <cstring> #include <limits> #include <string> #include <vector> #include <list> #include <set> #include <map> #include <queue> #include <stack> #include <algorithm> #include <cassert> using namespace std; int cache[11][11][11]; int DFS(int m, int last, int n, const int N) { // cout << "[debug]m: " << m << ", last: " << last << ", n: " << n << endl; if (n == 0) { assert (m == 0); return 1; } if (m == 0) { return 1; } if (cache[m][last][n] >= 0) { return cache[m][last][n]; } cache[m][last][n] = 0; for (int i = m / n; i <= min(last, m); ++i) { cache[m][last][n] += DFS(m - i, i, n - 1, N); } return cache[m][last][n]; } int main1() { int T, M, N; cin >> T; for (int m = 0; m <= 10; ++m) { for (int last = 0; last <= 10; ++last) { for (int n = 0; n <= 10; ++n) { cache[m][last][n] = -1; } } } for (int t = 0; t < T; ++t) { cin >> M >> N; DFS(M, M, N, N); cout << cache[M][M][N] << endl; } return 0; } int main() { int T, M, N; cin >> T; int DP[11][11]; for (int m = 1; m <= 10; ++m) { DP[m][0] = 0; } for (int n = 0; n <= 10; ++n) { DP[0][n] = 1; } for (int m = 1; m <= 10; ++m) { for (int n = 1; n <= 10; ++n) { if (m >= n) { DP[m][n] = DP[m - n][n] + DP[m][n - 1]; } else { DP[m][n] = DP[m][n - 1]; } // cout << "[debug]m: " << m << ", n: " << n << ", DP[m][n]: " << DP[m][n] << endl; } } for (int t = 0; t < T; ++t) { cin >> M >> N; cout << DP[M][N] << endl; } return 0; }