传送门

这道题有好多好多种做法呀……先说一下最暴力的,O(n^4的做法)

我们相当于要找两条从左上到右下的路,使路上的数字和最大。所以其实路径从哪里开始走并不重要,我们就直接假设全部是从左上出发的好啦。设dp[i][j][p][q]表示第一条路枚举到点(i,j),第二条路枚举到点(p,q)时,当前能取到的最大值。

这样dp方程很显然,就是dp[i][j][p][q] = max(dp[i-1][j][p-1][q],dp[i][j-1][p-1][q],sp[i-1][j][p][q-1],dp[i][j-1][p][q-1]) + a[i][j] + a[p][q].如果i==p && j == q,那么减去一个a[i][j].最后dp[m][n][m][n]即为结果。

四层循环枚举,复杂度O(n^4),上一下代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define rep(i,a,n) for(ll i = a;i <= n;i++)
#define per(i,n,a) for(ll i = n;i >= a;i--)
#define enter putchar('\n') using namespace std;
const int M = ;
typedef long long ll; int read()
{
int ans = ,op = ;
char ch = getchar();
while(ch < '' || ch > '')
{
if(ch == '-') op = -;
ch = getchar();
}
while(ch >= '' && ch <= '')
{
ans *= ;
ans += ch - '';
ch = getchar();
}
return ans * op;
}
int m,n,dp[][][][],a[][];
int main()
{
m = read(),n = read();
rep(i,,m)
rep(j,,n) a[i][j] = read();
rep(i,,m)
rep(j,,n)
rep(p,,m)
rep(q,,n)
{
dp[i][j][p][q] = max(max(dp[i-][j][p-][q],dp[i-][j][p][q-]),max(dp[i][j-][p-][q],dp[i][j-][p][q-])) + a[i][j] + a[p][q];
if(i == p && j == q) dp[i][j][p][q] -= a[i][j];
}
printf("%d\n",dp[m][n][m][n]);
return ;
}

之后我们说一下怎么优化。两条路在走的时候,因为每次只能向右或者下走一格,所以两者必然是在同一条斜对角线上的(一条从右上到左下的对角线)那么我们就可以用对角线的横纵坐标和来表示当前DP到了哪里,之后每次只要枚举两个点的横坐标就可以把路径表示出来(也就是表示出来当前枚举的是哪两个点)。这样的话就可以把DP过程的时空降到三维的。

具体的写法也很简单,直接看一下代码即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
typedef long long ll;
const int M = ;
int n,m,f[M][M],dp[][M][M],q[M];
int read()
{
int ans = ,op = ;
char ch = getchar();
while(ch < '' || ch > '')
{
if(ch == '-') op = -;
ch = getchar();
}
while(ch >='' && ch <= '')
{
ans *= ;
ans += ch - '';
ch = getchar();
}
return ans * op;
} int main()
{
m = read(),n = read();
rep(i,,m)
rep(j,,n) f[i][j] = read();
rep(i,,n+m)
{
rep(p,,i-)
{
if(p > m) break;
rep(q,,i-)
{
if(q > m) break;
dp[i][p][q] = max(max(dp[i-][p][q-],dp[i-][p-][q]),max(dp[i-][p-][q-],dp[i-][p][q])) + f[p][i-p] + f[q][i-q];
if(p == q) dp[i][p][q] -= f[p][i-p];
}
}
}
printf("%d\n",dp[n+m][m][m]);
return ;
}

之后,时间复杂度基本已经不能再优化,不过空间复杂度却可以优化到O(n^2)(其实还要再乘以一个常数)

我们从刚才的三维DP的转移过程来考虑一下。每次DP都是从上一条对角线上的元素转移过来,和其他的对角线没有任何关系,相当于我们在每次DP的时候只需要考虑两个,一个是i,一个是i-1,在i-1之前的其实已经没有了任何作用。所以我们完全可以废物再利用。因为每次i的值只改变1,所以可以使用按位与的方法把它变成0或者1,之后直接dp即可,而最后的答案就是dp[(m+n)&1][m][m].这样空间复杂度又会降一维。本题的数据范围小,如果数据范围较大,滚动数组对于空间的节省就极为有用了。

看一下代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
typedef long long ll;
const int M = ;
int n,m,f[M][M],dp[][M][M];
int read()
{
int ans = ,op = ;
char ch = getchar();
while(ch < '' || ch > '')
{
if(ch == '-') op = -;
ch = getchar();
}
while(ch >='' && ch <= '')
{
ans *= ;
ans += ch - '';
ch = getchar();
}
return ans * op;
} int main()
{
m = read(),n = read();
rep(i,,m)
rep(j,,n) f[i][j] = read();
rep(i,,n+m)
{
rep(p,,i-)
{
if(p > m) break;
rep(q,,i-)
{
if(q > m) break;
int k = (i-)&;
dp[i&][p][q] = max(max(dp[k][p][q-],dp[k][p-][q]),max(dp[k][p-][q-],dp[k][p][q])) + f[p][i-p] + f[q][i-q];
if(p == q) dp[i&][p][q] -= f[p][i-p];
}
}
}
printf("%d\n",dp[(n+m)&][m][m]);
return ;
}

NOIP2008 传纸条(DP及滚动数组优化)的更多相关文章

  1. dp,滚动数组优化

    51Nod1084矩阵取数问题 V2 题意: 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上.第1遍时只能向下和向右走,第2遍时只能向上和向左 ...

  2. NOIP2008传纸条[DP]

    题目描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运的是 ...

  3. HDU_1024.MaxSumPlusPlus(基础DP + 滚动数组优化讲解)

    这道题打破了我常规的做题思路,因为这是我刚开始训练DP,感觉这道题目好晕眼呀,emm其实就是感觉自己是真的菜...... 为什么说打破了我的做题思路呢,因为我平时看题解都是在已经AC或者完全不懂的情况 ...

  4. [BZOJ1044][HAOI2008]木棍分割 二分 + 单调队列优化dp + 滚动数组优化dp

    Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长 ...

  5. LG3004 「USACO2010DEC」Treasure Chest 区间DP+滚动数组优化

    问题描述 LG3004 题解 把拿走的过程反向,看做添加的过程,于是很显然的区间DP模型. 设\(opt_{i,j}\)代表区间\([i,j]\)中Bessie可以获得的最大值,显然有 \[opt_{ ...

  6. CodeForces 173C Spiral Maximum 记忆化搜索 滚动数组优化

    Spiral Maximum 题目连接: http://codeforces.com/problemset/problem/173/C Description Let's consider a k × ...

  7. 51nod 编辑距离 + 滚动数组优化

    这道题一开始觉得增加和删除会移动字符串的位置很不好做 两个字符串dp状态一般是第一个前i个和第二个前j个 #include<cstdio> #include<algorithm> ...

  8. HDU - 1024 Max Sum Plus Plus 最大m段子段和+滚动数组优化

    给定n个数字,求其中m段的最大值(段与段之间不用连续,但是一段中要连续) 例如:2 5 1 -2 2 3 -1五个数字中选2个,选择1和2 3这两段. dp[i][j]从前j个数字中选择i段,然后根据 ...

  9. poj1159 dp(滚动数组优化)

    H - 简单dp 例题扩展 Crawling in process... Crawling failed Time Limit:3000MS     Memory Limit:65536KB     ...

随机推荐

  1. Django 中的时间处理

    操作系统为OS X 10.9.2,Django为1.6.5. 1.时区 在setting.py文件中设置了时区 TIME_ZONE = 'Asia/Shanghai' # 设置时区为UTC+8 USE ...

  2. 使用ftrace学习linux内核函数调用

    http://www.cnblogs.com/pengdonglin137/articles/4752082.html 转载: http://blog.csdn.net/ronliu/article/ ...

  3. 【PowerShell 学习系列】-- 删除Win10自带应用

    Get-AppxPackage *3d* | Remove-AppxPackage Get-AppxPackage *camera* | Remove-AppxPackage Get-AppxPack ...

  4. VUE 自定义组件之间的相互通信

    一.自定义组件 1.全局自定义组件 我们在var vm = new Vue({});的上面并列写上Vue.component('自定义组件名',{组件对象});来完成全局自定义组件的声明.示例代码如下 ...

  5. BUPT 2012复试机考 2T

    题目描述 ​给你一个n*n的矩阵, , 求其矩阵的k次幂,即Pk 输入格式 第一行,一个整数T(0<T<=10),表示要求矩阵的个数. 接下来有T组数据,每组数据格式如下: 第一行:两个数 ...

  6. Bootstrap全局CSS样式之button和图片

    .btn-default--button的默认样式. .btn-primary--button的首选样式: .btn-success--button的成功样式: .btn-info--button的一 ...

  7. android binder 机制三(匿名Service)

    什么是匿名Service?凡是没有到ServiceManager上注冊的Service,都是匿名Service. 还是拿上一篇的样例来举例,看代码: status_t MediaPlayer::set ...

  8. zookeeper 服务端上下线,客户端感知

    package pfs.y2017.m11.zookeeper.demo03; import java.util.ArrayList; import java.util.List; import or ...

  9. 在线API

    JExcelApi http://jexcelapi.sourceforge.net/resources/javadocs/index.html Poi http://poi.apache.org/a ...

  10. 获取DOM父元素和子元素

    利用javascript可以遍历DOM树,这篇文章介绍用获取一个DOM元素的所有父节点和获取一个DOM元素的所以子孙节点. 1.获取所有父节点.用递归的方法,用parentNode属性. <!D ...