题目链接:[点这儿].
题意:
给你一个
1-9
的数字集合,要你从中挑选出n
的数字,构成n
位数,数字可重复挑选,构成的n
位数要满足一个条件:
- 任意相邻的两个数字之差的绝对值要小于等于
2
.问你有多少种构造方式.
解析:
很明显的线性动态规划,首先第
i
位数字的状态可以根据第i-1
位数字的状态转移过来;设
dp[i][j]
表示第前i
位且最后一位为j
的构造方案数,那么根据条件:任意相邻的两位数字之差的绝对值不超过2,可以得出状态转移方程:dpi,j=∑k=−22dpi−1,j+k(i<n,j∈S)
代码:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T, K = 1;
for (cin >> T; T--; K++) {
int m, n;
cin >> m >> n;
vector<int> arr(m);
vector<vector<int> > dp(n + 1, vector<int>(10, 0));
for (int i = 0; i < m; i++)
cin >> arr[i], dp[0][arr[i]] = 1;
for (int i = 1; i < n; i++)
for (size_t j = 0; j < arr.size(); j++)
for (int k = -2; k <= 2; k++)
if (arr[j] + k < 10 && arr[j] + k > 0)
dp[i][arr[j]] += dp[i - 1][arr[j] + k];
int ans = 0;
for (size_t i = 0; i < arr.size(); i++)
ans += dp[n - 1][arr[i]];
cout << "Case " << K << ": " << ans << endl;
}
return 0;
}
可以用一个
map
来代替数组,这样程序的写法会简单,且空间会减少,是一种好的写法.
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T, K = 1;
for (cin >> T; T--; K++) {
int m, n, x;
cin >> m >> n;
map<int, int> dp, tmp;
for (int i = 0; i < m; i++)
dp[cin >> x, x] = 1;
for (int i = 1; i < n; i++) {
for (map<int, int>::iterator it = dp.begin(); it != dp.end(); ++it)
for (int k = -2; k <= 2; k++)
tmp[it->first] += dp.find(it->first + k) == dp.end() ? 0 : dp[it->first + k];
dp.swap(tmp);
tmp.clear();
}
int ans = 0;
for (map<int, int>::iterator it = dp.begin(); it != dp.end(); ++it)
ans += it->second;
cout << "Case " << K << ": " << ans << endl;
}
return 0;
}