zoukankan      html  css  js  c++  java
  • P7074 [CSP-J2020] 方格取数

    Description

    给定一个 (n*m) 的矩阵,每个格子里有一个数,每次只能向上、下、右走一步,不能走走过的格子,问从左上角走到右下角可以取到的最大值。

    Limit

    (n,m le 1000)

    Solution

    Solution 1 DP

    这题与某年前的方格取数很像,但由于这题可以上下走,所以有后效性,要转换思路。

    发现虽然能上下乱跑,且走完一列之后只能向右走,所以可以把这个矩阵划分为 (m) 列,由此设方程。

    又因为不能走回头路,所以在进入新的一列之后,要么只向上走,要么只向下走,要么再向右走一列。

    设计状态:(f[i][j]) 表示在第i行,第j列的位置,且下一步必定向右走(即到 ((i,j+1)))可取到的最大价值

    很轻易地得到初始值:(f[i][1] = sum_{S=j}^{i} a[j][1])
    考虑每个点如何从前一列转移来:

    据这个图可知,每个点从左边来有三种方式:

    • 直接从左边走过来。(绿)

    • 先从左边跳到这一列,再从上面走下来。(红)

    • 先从左边跳到这一列,再从下面走上来。(蓝)

    对这几种情况分别转移,为了转移方便,我们可以把左边的那一种情况给并到其他两种情况,答案不会改变。

    上面转移情况:

    可以从第 (1) 行枚举到 (n) 行,维护这一列的前缀和,则上面每一个得到的值就是前面的列中且结束在第i行的最大值+这一列向下走的过程中取的数之和。

    设现在要求第 (j) 列第 (i) 行,已经枚举到了第(k)行,(temp) 就表示 (max){(f[i][j - 1])} + (sum_{S=k}^{j} a[i][S])

    (temp = max(temp, f[i][j - 1]) + a[i][j])

    转移方程: (f[i][j] = max(f[i][j], temp))

    下面转移情况:

    同上面转移,只不过反过来枚举 (i)

    (temp = max(temp, f[i][j - 1]) + a[i][j])

    转移方程(f[i][j] = max(f[i][j], temp))

    Code:

    // by youyou2007 Aug.
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <stack>
    #define REP(i, x, y) for(register int i = x; i < y; i++)
    #define rep(i, x, y) for(register int i = x; i <= y; i++)
    #define PER(i, x, y) for(register int i = x; i > y; i--)
    #define per(i, x, y) for(register int i = x; i >= y; i--)
    #define lc (k << 1)
    #define rc (k << 1 | 1)
    using namespace std;
    #define int long long//会爆int 
    const int N = 1005;
    int n, m;
    int f[N][N], a[N][N];
    signed main()
    {
    	scanf("%lld%lld", &n, &m);	
    	rep(i, 1, n)
    	{
    		rep(j, 1, m)
    		{
    			scanf("%lld", &a[i][j]);
    		}
    	}
    	memset(f, -0x3f, sizeof f);
    	f[1][0] = 0;
    	f[1][1] = a[1][1];
    	rep(j, 1, m)
    	{
    		int temp = -99999999999999999;
    		rep(i, 1, n)
    		{
    		//	if(i == 1 && j == 1) continue;
    			temp = max(temp, f[i][j - 1]) + a[i][j];
    			f[i][j] = max(f[i][j], temp);
    		}
    		temp = -9999999999999999;
    		per(i, n, 1)
    		{
    		//	if(i == 1 && j == 1) continue;
    			temp = max(temp, f[i][j - 1]) + a[i][j];
    			f[i][j] = max(f[i][j], temp);
    		}
    	}
    	cout << f[n][m];
    	return 0
    }
    
    

    Solution 2 记忆化搜索

    既然DP是顺推的,当然可以记忆化搜索逆推!

    (f[i][j][0]) 表示当前在第 (i) 行第 (j) 列且从格子上(0)/下(1)方走到该格子的最大价值

    则上下的这两种情况可以解决了。

    现在要考虑从左边过来的情况,但由于左边没有限制,所以从左上或左下来的转移都是可以的。

    所以

    • (f[i][j][0]=max(dfs(x - 1, y, 0), dfs(x, y - 1, 0), dfs(x, y - 1, 1)) + a[x][y])

    这是上一步从上方转移的情况,由于不能走回头路,所以不能从下方转移。上面三个搜索分别是上方、左上方、左下方转移来。

    • (f[x][y][1] = max(dfs(x + 1, y, 1), max(dfs(x, y - 1, 0), dfs(x, y - 1, 1))) + a[x][y])

    从下方转移同理,上面三个搜索分别是上方、左上方、左下方转移来。

    Code

    // by youyou2007 Aug.
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    #include <stack>
    #define REP(i, x, y) for(register int i = x; i < y; i++)
    #define rep(i, x, y) for(register int i = x; i <= y; i++)
    #define PER(i, x, y) for(register int i = x; i > y; i--)
    #define per(i, x, y) for(register int i = x; i >= y; i--)
    #define lc (k << 1)
    #define rc (k << 1 | 1)
    using namespace std;
    #define int long long 
    const int N = 1005;
    const int MINN = -999999999999999;
    int n, m;
    int f[N][N][3], a[N][N];
    int dfs(int x, int y, int from)
    {
        if(x <= 0 || y <= 0 || x > n || y > m) return MINN;
        if(f[x][y][from] != MINN) return f[x][y][from];
        if(from == 0)
        {
            return f[x][y][from] = max(dfs(x - 1, y, 0), max(dfs(x, y - 1, 0), dfs(x, y - 1, 1))) + a[x][y];
        }
        else
        {
            return f[x][y][from] = max(dfs(x + 1, y, 1), max(dfs(x, y - 1, 0), dfs(x, y - 1, 1))) + a[x][y];
        }
    }
    signed main()
    {
    	scanf("%lld%lld", &n, &m);	
    	rep(i, 1, n)
    	{
    		rep(j, 1, m)
    		{
    			scanf("%lld", &a[i][j]);
    	        f[i][j][0] = f[i][j][1] = MINN;
    		}
    	}
        f[1][1][0] = f[1][1][1] = a[1][1];
    	dfs(n, m, 0);
    	cout << max(f[n][m][1], f[n][m][0]);
    	return 0;
    }
    
    
  • 相关阅读:
    了解Whitehorse
    更新排行榜说明
    一个从Microsoft Word发表Blog文章的工具
    首页"进入我的博客"可以正常使用了
    ADO.NET: Close()与Dispose() 的讨论
    向大家致歉
    折腾了我一个下午及吃晚饭时间的问题
    [转帖]ASP.NET服务器端异步Web方法
    关于增加“收藏”功能的设想
    Mono 0.30发布了
  • 原文地址:https://www.cnblogs.com/pjxpjx/p/15184491.html
Copyright © 2011-2022 走看看