zoukankan      html  css  js  c++  java
  • 51nod 更难的矩阵取数问题 + 滚动数组优化

    这里要求要走到终点再走回来,可以转化为两个人走。

    那么我们可以先粗暴的设f[x1][y1][x2][y2]为第一个人走到(x1, y1), 第二个人走到(x2, y2)的最大价值。

    那么这样空间会很大,通过观察可以发现,一个走的步数=横坐标+纵坐标,因为走一步一定是横坐标

    或者纵坐标+1.

    那么我们就可以转化为f[step][x1][x2],可以退出y1 = step - x1, y2 = step - x2

    那么转移方程就很好求了

    f[step][x1][x2] = max(f[step-1][x1-1][x2], f[step-1][x1-1][x2-1], f[step-1][x1][x2-1], f[step-1][x1][x2]) + a[i][step-i]

    + (i == j ? 0 : a[j][step-j])

    这里要判断如果是同一个格子的话只加一次。

    实际这样已经可以过了,代码如下

    #include<cstdio>
    #include<algorithm>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    const int MAXN = 212;
    int f[MAXN*2][MAXN][MAXN];
    int a[MAXN][MAXN], n, m;
    
    void up(int& x, int a) { x = max(x, a); }
    
    int main()
    {
    	scanf("%d%d", &m, &n);
    	REP(i, 1, n + 1)
    		REP(j, 1, m + 1)
    			scanf("%d", &a[i][j]);
    	REP(k, 2, n + m + 1)
    	{
    		for(int i = 1; i <= n && i + 1 <= k; i++)
    			for(int j = 1; j <= n && j + 1 <= k; j++)
    			{
    				for(int r1 = -1; r1 <= 0; r1++)
    					for(int r2 = -1; r2 <= 0; r2++)
    						up(f[k][i][j], f[k - 1][i + r1][j + r2]);
    				f[k][i][j] += a[i][k-i] + (i == j ? 0 : a[j][k-j]);
    			}
    	}
    	printf("%d
    ", f[n + m][n][n]);
    	return 0;	
    } 

    但是呢其实空间上还可以更优化,因为只和k-1有关

    那么我这里想到两种方法实现滚动数组。

    第一个就是开两个二维数组,然后就来回更新。

    #include<cstdio>
    #include<algorithm>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    const int MAXN = 212;
    int f[2][MAXN][MAXN];
    int a[MAXN][MAXN], n, m;
    
    void up(int& x, int a) { x = max(x, a); }
    
    int main()
    {
    	scanf("%d%d", &m, &n);
    	REP(i, 1, n + 1)
    		REP(j, 1, m + 1)
    			scanf("%d", &a[i][j]);
    	int t = 0;
    	REP(k, 2, n + m + 1)
    	{
    		for(int i = 1; i <= n && i + 1 <= k; i++)
    			for(int j = 1; j <= n && j + 1 <= k; j++)
    			{
    				for(int r1 = -1; r1 <= 0; r1++)
    					for(int r2 = -1; r2 <= 0; r2++)
    						up(f[t][i][j], f[t ^ 1][i + r1][j + r2]);
    				f[t][i][j] += a[i][k-i] + (i == j ? 0 : a[j][k-j]);
    			}
    		t ^= 1;	
    	}
    	printf("%d
    ", f[t ^ 1][n][n]);
    	return 0;	
    } 

    还一种有点类似01背包逆推那个做法,只用一个数组就可以实现,改变循环顺序就好了。

    为了让当前状态转移的时候,用来更新的值都是上一行的,所以我们要逆序来操作。

    因为如果是从上到下,从左到右的话,要更新当前状态,需要用到f[i-1][j]等,而这个时候

    f[i-1][j]之前已经算过了,已经更新过了,只要更新过了就成了这一行的值了,就不行。

    所以我们要让f[i-1][j], f[i-1][j-1], f[i][j-1],f[i][j]都没有更新过。

    所以我们就从下到上,从右到左来推,这样就可以保证都是上一行的值了。

    #include<cstdio>
    #include<algorithm>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    const int MAXN = 212;
    int f[MAXN][MAXN];
    int a[MAXN][MAXN], n, m;
    
    void up(int& x, int a) { x = max(x, a); }
    
    int main()
    {
    	scanf("%d%d", &m, &n);
    	REP(i, 1, n + 1)
    		REP(j, 1, m + 1)
    			scanf("%d", &a[i][j]);
    	REP(k, 2, n + m + 1)
    	{
    		for(int i = min(n, k - 1); i >= 1; i--)
    			for(int j = min(n, k - 1); j >= 1; j--)
    			{
    				for(int r1 = -1; r1 <= 0; r1++)
    					for(int r2 = -1; r2 <= 0; r2++)
    						up(f[i][j], f[i + r1][j + r2]);
    				f[i][j] += a[i][k-i] + (i == j ? 0 : a[j][k-j]);
    			}
    	}
    	printf("%d
    ", f[n][n]);
    	return 0;	
    } 
  • 相关阅读:
    Windows Phone 8 Wallet 手机钱包 / 电子钱包
    Windows Phone 8 In app purchase 应用内购买 / 应用内支付
    Windows Phone 8 适应多屏分辨率
    Windows phone 8 基于定位的后台应用
    Windows Phone 8 Nokia地图控件
    Windows Phone 8 MDIL编译与代码混淆工具
    Windows Phone 8 近场通信 NFC / Bluetooth Proximity
    Windows Phone 8 镜头应用 Lenses for Windows Phone 8
    Windows Phone 8 与 windows 8 开发技术概览
    嵌入式成长轨迹54 【Zigbee项目】【CC2430基础实验】【系统睡眠工作状态】
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819437.html
Copyright © 2011-2022 走看看