SDUT 3554 无尽走廊 (动态规划) -- 解题报告

题面

无尽走廊
Time Limit: 1000MS Memory limit: 65536K

题目描述
2006年,我们可以称之为“帆“船年。一艘艘友谊的小船都在这一年翻掉了(当然也有升华为巨轮的)。然而这种事注定与小鱼无缘,就在不久前,小鱼与小驴刚吵了一架,在争吵的过程中,小鱼与小驴友谊的小木船失衡翻掉了。小鱼与小驴坠入大海,小鱼醒来后发现小驴不见了,慌张的小鱼四处寻找小驴,但小鱼发现小驴并不在小鱼身边。此时小鱼发现自己在一个大大的走廊尽头,走廊那边是无尽的黑暗,而小鱼身旁的一个告示牌上有如下内容:
亲爱的人类啊~欢迎来到海洋之邸~想要找到你的朋友么~那就穿越这条无尽走廊吧~下面有一张魔法地图~可以带你通向海底堡~你的朋友就在那里等着你那~下面有一张魔法地图~可以带你离开这里~
小鱼捡起地图,发现这条走廊是由好几条小走廊组成,地图上每条小走廊由一个大写英文字母表示。而小鱼现在在第一个字母处,出口在最后一个字母。
地图上说,每个小走廊都有一扇时空之门,可以跳转到当前走廊往后第1~k个走廊处。而所需要的时间为两走廊字母编号的距离。譬如A走廊与B走廊间花费为1 Z与A花费为25 C与A花费为2。
小鱼现在十分后悔跟小驴吵架。急切地想要找到小驴,你能帮小鱼算出一种方案,能够尽快找到小驴么?

输入
多组输入。
先输入一个整数T,表示组数。
之后每组先输入一个字符串(0 < 串长 <= 10^4),之后输入一个整数k(1 <= k <= 串长)表示每次最远移动距离。
每组输入占一行,以空格隔开。

输出
对于每组输入,输出一个正整数表示小鱼找到小驴的最短时间。每组输出占一行。

示例输入
3
ACA 1
ACA 2
ABCA 2

示例输出
4
0
2

提示
对于第二组输入,在第一个走廊处往后移动两步,到达尽头。
对于第三组输入,在第一个走廊处先移动一步到达第二个走廊,再移动两步到达尽头,时间最短。

来源
“师创杯”山东理工大学第八届ACM程序设计竞赛

解题思路

稍有常识的同学不难看出,这道题其实就是很简 (keng) 单 (die) 的 DP。我们令 dp[i] 表示到第 i 个走廊的最短时间,则 dp[i] 等于在 i 前面所有 k 个位置的「当前最短时间 + 此位置到 i 花费的时间」中的最小值。由此可写出状态转移方程:

dp[i]=min(dp[ij]+str[i]str[j]),j[ik,i)

当然,如果真的看到上面就去敲的话,恭喜你会 get 一个 TLE(出题人是 LeiQ,我只能提示到这了,抓到本体后你们懂的),这是因为每次内层循环都要跑 k(1<=k<=len) 次,对这个题的数据范围来说,如果是 O(nk) 的时间复杂度毫无疑问是要超时的。LeiQ 提示的解决方法使用队列来优化。于是,我们创建 26 个队列来存储 k 步内对应 A-Z 字母的下标有哪些,每次只需要遍历 26 种字母,取每个队列的队首元素,也即 k 步内对应字母的最短时间的下标,计算经此下标到 i 的时间,并不断更新最小值即可。遍历结束后,将当前下标 i 加入到对应字母的队列中。这样即可保证每个队列中存放的下标都是递增的,因此我们遍历时只取队首元素即可保证这个下标处记录的时间是此队列中所有下标里最短的。

这样我们就可以写出本题的代码了:

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int INF = 0x3f3f3f3f;

int main(int argc, char const *argv[]) {
    int dp[10001], t, k;
    char str[10001];
    queue<int> q[26];
    scanf("%d", &t);
    while(t--) {
        scanf("%s %d", str, &k);
        int len = strlen(str);
        for(int i=0; i<26; ++i) {
            while(!q[i].empty()) {
                q[i].pop();
            }
        }
        memset(dp, INF, sizeof(dp));
        dp[0] = 0;
        q[str[0]-'A'].push(0);
        for(int i=1; iint idx = str[i]-'A';
            for(int j=0; j<26; ++j) {
                //弹出不在可达范围内的下标
                if(!q[j].empty() && i-q[j].front()>k) q[j].pop();
                if(q[j].empty()) continue;
                dp[i] = min(dp[i], dp[q[j].front()]+abs(idx-j));
            }
            q[idx].push(i);
        }
        printf("%d\n", dp[len-1]);
    }

    return 0;
}

你可能感兴趣的:(解题报告)