POJ 3827 Facer is learning to swim

题目大意:

        现有一个游泳的游戏,游泳池是一个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];//格式字符串

void
dfs( 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 );
    }
}

int
main() {
	
    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        20

typedef    struct    { int money; char acc; } Pool;

Pool    p[MAXN][MAXM];
int        dp[MAXM];
int        n, m, k;

char    fmt[FMTLEN];

void
dfs( 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 );
    }
}

int
main() {

    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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值