【题意】:一只手,相对拇指不动,至少要占5个位置,至多可以弹9个键。拇指移动距离x需要花费floor(sqrt(x)),已知左右手的初始位置和接下来要弹奏的n个键,求弹完n个键的最小花费。
【题解】:比较明显的dp。
设dp[k][i][j]表示已经弹完k个键,左手在i,右手在j的最小花费。
思考一下,对于每一次弹的键来说,我们最多移动一只手,既不会同时移动两只手,显然两只手都移动肯定不是最优情况,所以不需要考虑。
对于一些没用状态,既弹不到当前的键,直接设为inf。
总的来说就是,这题无用状态很多,我们只需要处理有用的状态。
【代码】:
1 #include "iostream"
2 #include "cstdio"
3 #include "cstring"
4 #include "algorithm"
5 #include "vector"
6 #include "queue"
7 #include "cmath"
8 #include "string"
9 #include "cctype"
10 #include "map"
11 using namespace std;
12 #define pb push_back
13 #define lc(x) (x << 1)
14 #define rc(x) (x << 1 | 1)
15 #define lowbit(x) (x & (-x))
16 #define ll long long
17 #define maxn 1050
18 #define maxm 52
19 const int inf = 1 << 30;
20 const int gap = 9;
21 int dp[maxn][maxm][maxm];
22 int l, r, n, key;
23 int isqrt[100];
24
25 void init() {
26 //抄了watashi的平方根预处理,写得太漂亮了
27 for(int i = 0; i < 10; i++)
28 for(int j = i * i; j < (i + 1) * (i + 1); j++)
29 isqrt[j] = i;
30 }
31
32 int main() {
33 while(~scanf("%d%d%d", &l, &r, &n)) {
34 init();
35 for(int k = 0; k < maxn; k++)
36 for(int i = 0; i < maxm; i++)
37 for(int j = 0; j < maxm; j++)
38 dp[k][i][j] = inf;
39 dp[0][l][r] = 0;
40 for(int now = 0; now < n; now++) {
41 scanf("%d", &key);
42 for(int i = 0; i < maxm; i++) {
43 for(int j = 0; j < maxm; j++) {
44 if(dp[now][i][j] == inf) continue;
45 for(int k = key; k - key < gap && k < maxm; k++)
46 dp[now+1][k][j] = min(dp[now+1][k][j], dp[now][i][j] + isqrt[abs(i-k)]);
47 for(int k = key; key - k < gap && k >= 0; k--)
48 dp[now+1][i][k] = min(dp[now+1][i][k], dp[now][i][j] + isqrt[abs(j-k)]);
49 }
50 }
51 }
52 int ans = inf;
53 for(int i = 0; i < maxm; i++)
54 for(int j = 0; j < maxm; j++)
55 ans = min(dp[n][i][j], ans);
56 printf("%d\n", ans);
57 }
58 return 0;
59 }
60