您的位置:首页 > 其它

SOJ 2818 QQ音速 (DP)

2015-10-23 07:51 288 查看
@(K ACMer)

题意:

两个手指按在方向键上,每次变换手指的位置会有相应地花费,给你一个需要按的方向键的序列,问你完成这些序列所需要对最小花费是多少?

分析:

拿到这道题首先想暴力搜索的方法:dfs(x, i, j),表示当前在字符串的x位,左手放在i位置,右手放在j位置走到结尾需要对步数.然后就可以很容易写一个复杂度为O(2n)的dfs了.

显然要TLE啦~,这里有很多重复计算的,开个DP数组把已经算过的记录下来,这就叫记忆优化搜索,其本质就是DP,只不过是递归的实现,转化为循环实现就是标准的DP了.这个题递归的实现不能过因为,其递归次数过多,会爆栈!

定义状态为:dp[k][i][j]为在移动到序列的第k位,左手放在i位置,右手放在j位置所需的最小花费.然后该状态由其子状态dp[k - 1][t][j]或者dp[k - 1][i][t]转移而来(注意这里的t值得是str[k - 1],因为你的上一次必须放在正确的位置.),,转移方程如下.

dp[k][i][j]=min(dp[k−1][t][j]+w[t][i],dp[k−1][i][t]+w[t][j])

总结:

DP并不是来就是瞎猜方程,而是要先从最暴力的方法优化过来,其核心实现就是把已经计算过的不要重复计算,也就是记忆优化.递推方程要把该问题,转移到其子问题上

code:

先上比较直观的记忆优化搜索的代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n;
const int maxn = (int)1e6 + 200;
char str[maxn];
int w[4][4] ={{0, 1, 2, 2}, {1, 0, 1, 1}, {2, 1, 0, 2}, {2, 1, 2, 0}};
int dp[4][4][maxn];

int dfs(int x, int a, int b, int v) {
if (x == n) return v;
if (dp[a][b][x] != -1) return dp[a][b][x];
int key = str[x] - '0';
return dp[a][b][x] = min(dfs(x + 1, key, b, w[a][key]), dfs(x + 1, a, key, w[b][key])) + v;
}

int main(void) {
while (~scanf("%s", str)) {
n = strlen(str);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < n; k++) {
dp[i][j][k] = -1;
}
}
}
memset(dp, -1, sizeof(dp));
printf("%d\n", dfs(0, 2, 3, 0));
}
return 0;
}


DP的代码:

#include <cstdio>
#include <cstring>
using namespace std;

int n;
const int maxn = (int)1e6 + 200, INF = 0x3fffffff;
char str[maxn];
int w[4][4] ={{0, 1, 2, 2}, {1, 0, 1, 1}, {2, 1, 0, 2}, {2, 1, 2, 0}};
int dp[maxn][4][4];
#define mins(x, y) (x) < (y) ? (x) : (y)

int main(void) {
while (scanf("%s", str) == 1) {
n = strlen(str);
for (int i = 0 ;i < 4; i++) {
for (int j = 0; j < 4; j++) {
dp[0][i][j] = w[2][i] + w[3][j];
}
}
for (int k = 1; k <= n; k++) {
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
//                    if (k < n && str[k] - '0' != i && str[k] - '0' != j) continue;
int t = str[k - 1] - '0';
dp[k][i][j] = mins(dp[k - 1][t][j] + w[t][i], dp[k - 1][i][t] + w[t][j]);
}
}
}
int ans = INF;
for (int i = 0; i < 4; i++) {
ans = mins(ans, dp
[i][str[n - 1] - '0']);
ans = mins(ans, dp
[str[n - 1] - '0'][i]);
}
printf("%d\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: