【动态规划】【NOI2005】瑰丽华尔兹

【任务描述】 
你跳过华尔兹吗?当音乐响起,当你随着旋律滑动舞步,是不是有一种漫步仙境的惬意? 众所周知,跳华尔兹时,最重要的是有好的音乐。但是很少有几个人知道,世界上最伟大的钢琴家一生都漂泊在大海上, 他的名字叫丹尼· 布德曼· T.D.·柠檬·1900,朋友们都叫他1900。 1900 在 20 世纪的第一年出生在往返于欧美的邮轮弗吉尼亚号上。很不幸,他刚出生就被抛弃,成了孤儿。1900 孤独的成长在弗吉尼亚号上,从未离开过这个摇晃的世界。也许是对他命运的补偿,上帝派可爱的小天使艾米丽照顾他。  可能是天使的点化,1900拥有不可思议的钢琴天赋:从未有人教,从没看过乐谱,但他却能凭着自己的感觉弹出最沁人心脾的旋律。当 1900 的音乐获得邮轮上所有人的欢迎时,他才 8 岁,而此时,他已经乘着海轮往返欧美大陆 50 余次了。 
 
虽说是钢琴奇才,但1900还是个孩子, 他有着和一般男孩一样的好奇和调皮,只不过更多一层浪漫的色彩罢了: 这是一个风雨交加的夜晚,海风卷起层层巨浪拍打着弗吉尼亚号,邮轮随着巨浪剧烈的摇摆。船上的新萨克斯手迈克斯·托尼晕船了,1900 招呼托尼和他一起坐到舞厅里的钢琴上,然后松开了固定钢琴的闸,于是,钢琴随着海轮的倾斜滑动起来。准确的说,我们的主角1900、钢琴、邮轮随着1900的旋律一起跳起了华尔兹,随着“嘣嚓嚓”的节奏,托尼的晕船症也奇迹般的消失了。后来托尼在回忆录上这样写道: 
大海摇晃着我们 使我们转来转去 
快速的掠过灯和家具 
我意识到我们正在和大海一起跳舞 
真是完美而疯狂的舞者 

晚上在金色的地板上快乐的跳着华尔兹是不是很惬意呢?也许,我们忘记了一个人,那就是艾米丽,她可没闲着:她必须在适当的时候施展魔法帮助1900,不让钢琴碰上舞厅里的家具。 

不妨认为舞厅是一个N行M列的矩阵, 矩阵中的某些方格上堆放了一些家具,其他的则是空地。钢琴可以在空地上滑动,但不能撞上家具或滑出舞厅,否则会损坏钢琴和家具,引来难缠的船长。 每个时刻,钢琴都会随着船体倾斜的方向向相邻的方格滑动一格,相邻的方格可以是向东、向西、向南或向北的。而艾米丽可以选择施魔法或不施魔法:如果不施魔法,则钢琴会滑动;如果施魔法,则钢琴会原地不动。 艾米丽是个天使,她知道每段时间的船体的倾斜情况。她想使钢琴在舞厅里滑行的路程尽量长,这样 1900 会非常高兴,同时也有利于治疗托尼的晕船。但艾米丽还太小,不会算,所以希望你能帮助她。 
【输入格式】 
输入文件的第一行包含5个数 N, M,  x,  y和K。N和M描述舞厅的大小,x和y为钢琴的初始位置;我们对船体倾斜情况是按时间的区间来描述的,且从1开始计算时间,比如“在[1, 3]时间里向东倾斜,[4, 5]时间里向北倾斜” ,因此这里的K表示区间的数目。 
以下N行, 每行M个字符, 描述舞厅里的家具。第i行第j列的字符若为‘ . ’,则表示该位置是空地;若为‘ x ’,则表示有家具。 
以下K行,顺序描述 K个时间区间,格式为:si ti di(1 ≤ i ≤ K)。表示在时间区间[si, ti]内,船体都是向di方向倾斜的。di为 1, 2, 3, 4中的一个,依次表示北、南、西、东(分别对应矩阵中的上、下、左、右) 。输入保证区间是连续的,即 
s1 = 1 
ti = si-1 + 1   (1 < i ≤ K) 
tK = T
【输出格式】 
输出文件仅有1行,包含一个整数,表示钢琴滑行的最长距离(即格子数)。  
【输入样例】 
4 5 4 1 3 
..xx. 
..... 
...x. 
..... 
1 3 4 
4 5 1 
6 7 2 
【输出样例】 
6 
【样例说明】 
钢琴的滑行路线: 

 

钢琴在“×”位置上时天使使用一次魔法,因此滑动总长度为6。 
【评分方法】 
本题没有部分分,你的程序的输出只有和我们的答案完全一致才能获得满
分,否则不得分。 
【数据范围】 
50%的数据中,1≤N, M≤200,T≤200;
100%的数据中,1≤N, M≤200,K≤200,T≤40000。

先贴一个不加单调队列优化的程序,在衡阳八中oj,天津大学oj,华东师大oj等各个oj都能通过。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxN = 210;
const int dx[] = {0, -1, 1, 0, 0};
const int dy[] = {0, 0, 0, -1, 1};

bool mp[maxN][maxN];
int Lim[maxN][maxN][5];
int f[2][maxN][maxN]; //零壹滚动。
int n, m, K, x, y, L, R, dir, pst, ths = 1;

int main()
{
    freopen("adv1900.in", "r", stdin);
    freopen("adv1900.out", "w", stdout);
    scanf("%d%d%d%d%d", &n, &m, &x, &y, &K);
    for (int i = 1; i < n + 1; ++i)
    {
        scanf("\n");
        for (int j = 1; j < m + 1; ++j)
            mp[i][j] = getchar() == '.';
    }
    memset(Lim, 0xff, sizeof(Lim));
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
    if (mp[i][j])
    {
        Lim[i][j][2] = Lim[i - 1][j][2] + 1;
        Lim[i][j][4] = Lim[i][j - 1][4] + 1;
    }
    for (int i = n; i > 0; --i)
    for (int j = m; j > 0; --j)
    if (mp[i][j])
    {
        Lim[i][j][1] = Lim[i + 1][j][1] + 1;
        Lim[i][j][3] = Lim[i][j + 1][3] + 1;
    }
	//先预处理出每个点往上下左右四个方向
	//能够达到的最大距离。
    memset(f, ~0x3f, sizeof(f));
    f[ths][x][y] = 0;
    for (int k = 1; k < K + 1; ++k)
    {
        scanf("%d%d%d", &L, &R, &dir);
        swap(pst, ths);
        int len = R - L + 1;
        for (int i = 1; i < n + 1; ++i)
        for (int j = 1; j < m + 1; ++j)
        {
            f[ths][i][j] = ~0x3f3f3f3f;
            for (int p = 0; p <= len
                 && p <= Lim[i][j][dir]; ++p)
                f[ths][i][j] =
                max(f[ths][i][j], p +
                    f[pst][i - dx[dir] *
                    p][j - dy[dir] * p]);
        }
    }
    int ans = 0;
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
        ans = max(ans, f[ths][i][j]);
    printf("%d", ans);
    return 0;
}

再贴一个用单调队列优化过的程序,代码似乎更长……

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxN = 210;
const int dx[] = {0, -1, 1, 0, 0};
const int dy[] = {0, 0, 0, -1, 1};

bool mp[maxN][maxN];
int Lim[maxN][maxN][5];
int F[2][maxN][maxN];
int q[maxN], val[maxN];
int n, m, K, x, y, L, R, dir, pst, ths = 1, f, r;

int main()
{
    freopen("adv1900.in", "r", stdin);
    freopen("adv1900.out", "w", stdout);
    scanf("%d%d%d%d%d", &n, &m, &x, &y, &K);
    for (int i = 1; i < n + 1; ++i)
    {
        scanf("\n");
        for (int j = 1; j < m + 1; ++j)
            mp[i][j] = getchar() == '.';
    }
    memset(Lim, 0xff, sizeof(Lim));
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
    if (mp[i][j])
    {
        Lim[i][j][2] = Lim[i - 1][j][2] + 1;
        Lim[i][j][4] = Lim[i][j - 1][4] + 1;
    }
    for (int i = n; i > 0; --i)
    for (int j = m; j > 0; --j)
    if (mp[i][j])
    {
        Lim[i][j][1] = Lim[i + 1][j][1] + 1;
        Lim[i][j][3] = Lim[i][j + 1][3] + 1;
    }
    memset(F, ~0x3f, sizeof(F));
    F[ths][x][y] = 0;
    for (; K; --K)
    {
        scanf("%d%d%d", &L, &R, &dir);
        swap(pst, ths);
        memset(F[ths], ~0x3f, sizeof(F[ths]));
        int len = R - L + 1;
        switch (dir)
        {
        case 4:
            for (int i = 1; i < n + 1; ++i)
            {
                f = r = 0;
                for (int j = 1; j < m + 1; ++j)
                {
                    int tmp = min(len, Lim[i][j][dir]);
                    while (f < r && j - q[f] > tmp) ++f;
                    while (f < r && F[pst][i][j]
                           - j >= val[r - 1]) --r;
                    if (F[pst][i][j] > -1)
                    {
                        q[r] = j;
                        val[r++] = F[pst][i][j] - j;
                    }
                    if (f < r)
                        F[ths][i][j] = max(F[ths][i][j],
                                           val[f] + j);
                }
            }
            break;
        case 2:
            for (int j = 1; j < m + 1; ++j)
            {
                f = r = 0;
                for (int i = 1; i < n + 1; ++i)
                {
                    int tmp = min(len, Lim[i][j][dir]);
                    while (f < r && i - q[f] > tmp) ++f;
                    while (f < r && F[pst][i][j]
                           - i >= val[r - 1]) --r;
                    if (F[pst][i][j] > -1)
                    {
                        q[r] = i;
                        val[r++] = F[pst][i][j] - i;
                    }
                    if (f < r)
                        F[ths][i][j] = max(F[ths][i][j],
                                           val[f] + i);
                }
            }
            break;
        case 3:
            for (int i = n; i > 0; --i)
            {
                f = r = 0;
                for (int j = m; j > 0; --j)
                {
                    int tmp = min(len, Lim[i][j][dir]);
                    while (f < r && q[f] - j > tmp) ++f;
                    while (f < r && F[pst][i][j]
                           + j >= val[r - 1]) --r;
                    if (F[pst][i][j] > -1)
                    {
                        q[r] = j;
                        val[r++] = F[pst][i][j] + j;
                    }
                    if (f < r)
                        F[ths][i][j] = max(F[ths][i][j],
                                           val[f] - j);
                }
            }
            break;
        case 1:
            for (int j = m; j > 0; --j)
            {
                f = r = 0;
                for (int i = n; i > 0; --i)
                {
                    int tmp = min(len, Lim[i][j][dir]);
                    while (f < r && q[f] - i > tmp) ++f;
                    while (f < r && F[pst][i][j]
                           + i >= val[r - 1]) --r;
                    if (F[pst][i][j] > -1)
                    {
                        q[r] = i;
                        val[r++] = F[pst][i][j] + i;
                    }
                    if (f < r)
                        F[ths][i][j] = max(F[ths][i][j],
                                           val[f] - i);
                }
            }
            break;
        }
    }
    int ans = 0;
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
        ans = max(ans, F[ths][i][j]);
    printf("%d\n", ans);
    return 0;
}

第二次做,朴素:

/***************************\
 * @prob: NOI2005 adv1900  *
 * @auth: Wang Junji       *
 * @stat: TLE: 70          *
 * @date: June. 3rd, 2012  *
 * @memo: 动态规划          *
\***************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

const int maxN = 210, INF = 0x3f3f3f3f;
const int dx[] = {0, -1, 1, 0, 0}, dy[] = {0, 0, 0, -1, 1};
bool mp[maxN][maxN];
int f[2][maxN][maxN], Lim[5][maxN][maxN], n, m, x0, y0, K;

int main()
{
    freopen("adv1900.in", "r", stdin);
    freopen("adv1900.out", "w", stdout);
    scanf("%d%d%d%d%d", &n, &m, &x0, &y0, &K);
    for (int i = 1; i < n + 1; ++i)
    {
        scanf("\n");
        for (int j = 1; j < m + 1; ++j)
            mp[i][j] = getchar() == '.';
    }
    memset(Lim, 0xff, sizeof Lim);
    memset(f, ~0x3f, sizeof f);
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
    if (mp[i][j])
        Lim[4][i][j] = Lim[4][i][j - 1] + 1,
        Lim[2][i][j] = Lim[2][i - 1][j] + 1;
    for (int i = n; i; --i)
    for (int j = m; j; --j)
    if (mp[i][j])
        Lim[3][i][j] = Lim[3][i][j + 1] + 1,
        Lim[1][i][j] = Lim[1][i + 1][j] + 1;
    int pst = 0, ths = 1;
    f[ths][x0][y0] = 0;
    for (int k = 0, L, R, dir; k < K; ++k)
    {
        pst ^= 1, ths ^= 1;
        scanf("%d%d%d", &L, &R, &dir);
        int len = R - L + 1, t;
        for (int i = 1; i < n + 1; ++i)
        for (int j = 1; j < m + 1; ++j)
        for (f[ths][i][j] = f[pst][i][j], t = 1; t < len + 1
                && t < Lim[dir][i][j] + 1; ++t)
            f[ths][i][j] = std::max(f[ths][i][j],
                f[pst][i - t * dx[dir]][j - t * dy[dir]] + t);
    }
    int ans = 0;
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
        ans = std::max(ans, f[ths][i][j]);
    printf("%d\n", ans); return 0;
}
单调队列优化:
/**********************************\
 * @prob: NOI2005 adv1900         *
 * @auth: Wang Junji              *
 * @stat: Accepted.               *
 * @date: June. 3rd, 2012         *
 * @memo: 动态规划、单调队列优化     *
\**********************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

const int maxN = 210, INF = 0x3f3f3f3f;
const int dx[] = {0, -1, 1, 0, 0}, dy[] = {0, 0, 0, -1, 1};
bool mp[maxN][maxN];
int F[2][maxN][maxN], Lim[5][maxN][maxN], n, m, x0, y0, K;

int main()
{
    freopen("adv1900.in", "r", stdin);
    freopen("adv1900.out", "w", stdout);
    scanf("%d%d%d%d%d", &n, &m, &x0, &y0, &K);
    for (int i = 1; i < n + 1; ++i)
    {
        scanf("\n");
        for (int j = 1; j < m + 1; ++j)
            mp[i][j] = getchar() == '.';
    }
    memset(Lim, 0xff, sizeof Lim);
    memset(F, ~0x3f, sizeof F);
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
    if (mp[i][j])
        Lim[4][i][j] = Lim[4][i][j - 1] + 1,
        Lim[2][i][j] = Lim[2][i - 1][j] + 1;
    for (int i = n; i; --i)
    for (int j = m; j; --j)
    if (mp[i][j])
        Lim[3][i][j] = Lim[3][i][j + 1] + 1,
        Lim[1][i][j] = Lim[1][i + 1][j] + 1;
    int pst = 0, ths = 1;
    F[ths][x0][y0] = 0;
    for (int k = 0, L, R, dir; k < K; ++k)
    {
        pst ^= 1, ths ^= 1;
        scanf("%d%d%d", &L, &R, &dir);
        memset(F[ths], ~0x3f, sizeof F[ths]);
        int len = R - L + 1;
        static int q[maxN], val[maxN];
        switch (dir)
        {
        case 1:
            for (int j = 1; j < m + 1; ++j)
            {
                int f = 0, r = 0; q[r++] = n + 1;
                for (int i = n; i; --i) if (mp[i][j])
                {
                    while (f < r && q[f] - i > std::min(len, Lim[dir][i][j])) ++f;
                    while (f < r && F[pst][i][j] + i >= val[r - 1]) --r;
                    q[r] = i, val[r++] = F[pst][i][j] + i;
                    F[ths][i][j] = val[f] - i;
                }
            }
            break;
        case 2:
            for (int j = 1; j < m + 1; ++j)
            {
                int f = 0, r = 0; q[r++] = 0;
                for (int i = 1; i < n + 1; ++i) if (mp[i][j])
                {
                    while (f < r && i - q[f] > std::min(len, Lim[dir][i][j])) ++f;
                    while (f < r && F[pst][i][j] - i >= val[r - 1]) --r;
                    q[r] = i, val[r++] = F[pst][i][j] - i;
                    F[ths][i][j] = val[f] + i;
                }
            }
            break;
        case 3:
            for (int i = 1; i < n + 1; ++i)
            {
                int f = 0, r = 0; q[r++] = m + 1;
                for (int j = m; j; --j) if (mp[i][j])
                {
                    while (f < r && q[f] - j > std::min(len, Lim[dir][i][j])) ++f;
                    while (f < r && F[pst][i][j] + j >= val[r - 1]) --r;
                    q[r] = j, val[r++] = F[pst][i][j] + j;
                    F[ths][i][j] = val[f] - j;
                }
            }
            break;
        case 4:
            for (int i = 1; i < n + 1; ++i)
            {
                int f = 0, r = 0; q[r++] = 0;
                for (int j = 1; j < m + 1; ++j) if (mp[i][j])
                {
                    while (f < r && j - q[f] > std::min(len, Lim[dir][i][j])) ++f;
                    while (f < r && F[pst][i][j] - j >= val[r - 1]) --r;
                    q[r] = j, val[r++] = F[pst][i][j] - j;
                    F[ths][i][j] = val[f] + j;
                }
            }
            break;
        }
    }
    int ans = 0;
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
        ans = std::max(ans, F[ths][i][j]);
    printf("%d\n", ans); return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值