POJ 3827 Facer is learning to swim

来源:互联网 发布:网络音视频许可证 编辑:程序博客网 时间:2024/05/21 00:48

题目大意:

        现有一个游泳的游戏,游泳池是一个N×M的网格(1 ≤ N ≤ 100,1 ≤ M ≤ 1,000),左上角坐标为(1, 1),右下角坐标为(N, M),其中第一行为水面(可以在水面换气),下面的行都是水下,其中第M列的水下都是毒药,游进去会死。现在要从(1, 1)游到(1, M),现有恒定的横下速度1,即每次都会向右前进一格(也可以说是每秒前进一格),纵向速度为v(可变),如果v大于0表示下降潜水,v小于0表示上浮,v等于0表示纵向不动,纵向在下一秒移动的格数为|v|,但是如果x + v <1,其中x为行坐标y为列坐标(为游泳者的当前位置),则下一秒他会达到(1, y + 1),因为他不会飞上天,同样如果x + v > N,则他下一秒的坐标为(N, y + 1),因为他不会钻地。

        游泳者每隔k秒就需要到水面换一次气,即在水下时间不得超过k - 1秒(1 ≤ k ≤ 10)。

        在每个格子中必会出现两样东西中的一样(不会两个同时出现),一个就是加速器另一个就是钱,加速器可以改变纵向速度(即给纵向速度一个加速度A,-20 ≤ A ≤ 20),而遇到钱就可以捡,钱的值为p(-1,000,000 ≤ p ≤ 1,000,000),但是游泳者还随身携带了一种东西,就是小型变速器,他可以使用无限次,并且在每个格子中最多使用一次(或者不使用),小型变速器只能改变纵向速度,只能边-1或+1,如果加速值为Q的话,则Q = 0, ±1。

        现对加速器规则做详细说明(纵向合速度为v + a + q):

        1. 如果到达水面,则加速器失效并且垂直速度变为0,如果想在下一秒潜入水下则必须使用随身携带的小型加速器;

        2. 当到达底层后遇到加速器,在下一秒其纵向合速度为_v = v + a + q,并且_v + x  > N,由于不能钻地,则其下一秒移动到(N, y + 1) 并且_v不变,即在下一个格子总纵向速度仍然为_v,如果在下一个格子中遇到a,则在下一个格子中速度变为_v + a + q。

        现在需要合理地使用小型加速器使得游泳者到达(1, M)后能获得最大钱数。

        现有多个测例,测例以"0 0 0"结束,每个测例先给出N, M, K,接着给出一个N×M的矩阵,矩阵中元素的形式为"vA"或者"$P“,每个元素以空格隔开,v表示加速器,A为其值,$表示钱,P为钱数,对于每个测例输出到达(1, M)可以获得的最大钱数。

题目链接

注释代码:

/*                                                 * Problem ID : POJ 3827 Facer is learning to swim * Author     : Lirx.t.Una                                                 * Language   : C                                * Run Time   : 1969 ms                                                 * Run Memory : 924 KB                                                */ #include <stdio.h>#define    INF            2000001//分别为泳池长宽的最大值//都从下标1开始计#define    MAXN        101#define    MAXM        1001#define    FMTLEN        20//表示泳池中的格子//money是每个格子中的钱数(可以为负)//acc是accelerator,即加速器表示格子中加速装置对当前速度的改变值(可以为负)//acc范围为[-20, 20]在char的范围内(由于结构体的对齐使用int也无妨)typedef    struct    { int money; char acc; } Pool;Pool   p[MAXN][MAXM];//pool,表示泳池矩阵int        dp[MAXM];//dp[i]表示当Facer第i秒到达水面(最上层)时获得的最大钱数int        n, m, k;//表示泳池的长、宽以及Facer最多在水面下能连续待k - 1秒char    fmt[FMTLEN];//格式字符串voiddfs( int dpth, int x, int y, int tot, int v ) {//有界深度优先搜索//dpth即深度不能超过Facer的水下时间限制k,dpth = 0表示搜索的开始(即一个新的周期)  //此时Facer在水面//x表示横向坐标y为纵向坐标(表示水深)//tot表示Facer当前的所有钱数//v表示当前的垂直速度//x > m表示当前Facer已经游出泳池的边界了(超出第m列了)//dpth == k && y > 1,表示当前(即第k秒)Facer仍然在水下,即被淹死了    if ( x > m || dpth == k && y > 1 ) return ;//不能钻地以及浮在空中    if ( y < 1 ) y = 1;    else if ( y > n ) y = n;    tot += p[y][x].money;    if ( dpth && 1 == y ) {//如果Facer离开了起始位置并且又回到了水面//则可以跟新dp值并返回//即每个dp值最多只能影响其后面k个dp值        if ( tot > dp[x] ) dp[x] = tot;        return ;    }    if ( !dpth ) {//如果当前是搜索的开始//在水面上加速器不起作用(!!加速器只能改变垂直速度)        dfs( dpth + 1, x + 1, y, tot, 0 );//所以只能靠speedo调速,只能往左        dfs( dpth + 1, x + 1, y + 1, tot, 1 );//或者往下    }    else {//表示在水面下        v += p[y][x].acc;//考虑加速度//再考虑speedo的使用(无非就是上、不动、下)        dfs( dpth + 1, x + 1, y + v - 1, tot, v - 1 );        dfs( dpth + 1, x + 1, y + v, tot, v );        dfs( dpth + 1, x + 1, y + v + 1, tot, v + 1 );    }}intmain() {    int        i, j;//计数变量    while ( scanf("%d%d%d", &n, &m, &k), n ) {        for ( i = 1; i <= n; i++ )            for ( j = 1; j <= m; j++ ) {                scanf("%s", fmt);                if ( '$' == *fmt ) {                    sscanf(fmt, "$%d", &p[i][j].money);                    p[i][j].acc = 0;                }                else {                    sscanf(fmt, "v%d", &p[i][j].acc);                    p[i][j].money = 0;                }            }dp[1] = p[1][1].money;//起始位置就赋予该位置的钱数即可for ( i = 2; i <= m; i++ ) dp[i] = -INF;//都初始化为负无穷for ( i = 1; i < m; i++ )//m可以不用搜索,因为它是终点if ( dp[i] > -INF )//如果等于-INF,表示水面上第i列不可达(即不能从水面的前面几列到达该列)//因此从i开始搜索不仅没有意义而且会影响结果的正确性dfs( 0, i, 1, dp[i] - p[1][i].money, 0 );printf("%d\n", dp[m]);//直接输出到达第m列水面的最大钱数,第m列大于1行都是有毒的,游进去会死    }    return 0;}

无注释代码:

#include <stdio.h>#define    INF            2000001#define    MAXN        101#define    MAXM        1001#define    FMTLEN        20typedef    struct    { int money; char acc; } Pool;Pool    p[MAXN][MAXM];int        dp[MAXM];int        n, m, k;char    fmt[FMTLEN];voiddfs( int dpth, int x, int y, int tot, int v ) {    if ( x > m || dpth == k && y > 1 ) return ;    if ( y < 1 ) y = 1;    else if ( y > n ) y = n;    tot += p[y][x].money;    if ( dpth && 1 == y ) {            if ( tot > dp[x] ) dp[x] = tot;        return ;    }    if ( !dpth ) {            dfs( dpth + 1, x + 1, y, tot, 0 );        dfs( dpth + 1, x + 1, y + 1, tot, 1 );    }    else {            v += p[y][x].acc;        dfs( dpth + 1, x + 1, y + v - 1, tot, v - 1 );        dfs( dpth + 1, x + 1, y + v, tot, v );        dfs( dpth + 1, x + 1, y + v + 1, tot, v + 1 );    }}intmain() {    int        i, j;    while ( scanf("%d%d%d", &n, &m, &k), n ) {            for ( i = 1; i <= n; i++ )            for ( j = 1; j <= m; j++ ) {                            scanf("%s", fmt);                if ( '$' == *fmt ) {                    sscanf(fmt, "$%d", &p[i][j].money);                    p[i][j].acc = 0;                }                else {                    sscanf(fmt, "v%d", &p[i][j].acc);                    p[i][j].money = 0;                }            }        dp[1] = p[1][1].money;        for ( i = 2; i <= m; i++ ) dp[i] = -INF;        for ( i = 1; i < m; i++ )            if ( dp[i] > -INF )                dfs( 0, i, 1, dp[i] - p[1][i].money, 0 );        printf("%d\n", dp[m]);    }    return 0;}
0 0