NOIP2008 传纸条(DP及滚动数组优化)
这道题有好多好多种做法呀……先说一下最暴力的,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及滚动数组优化)的更多相关文章
- dp,滚动数组优化
51Nod1084矩阵取数问题 V2 题意: 一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上.第1遍时只能向下和向右走,第2遍时只能向上和向左 ...
- NOIP2008传纸条[DP]
题目描述 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题.一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了.幸运的是 ...
- HDU_1024.MaxSumPlusPlus(基础DP + 滚动数组优化讲解)
这道题打破了我常规的做题思路,因为这是我刚开始训练DP,感觉这道题目好晕眼呀,emm其实就是感觉自己是真的菜...... 为什么说打破了我的做题思路呢,因为我平时看题解都是在已经AC或者完全不懂的情况 ...
- [BZOJ1044][HAOI2008]木棍分割 二分 + 单调队列优化dp + 滚动数组优化dp
Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长 ...
- LG3004 「USACO2010DEC」Treasure Chest 区间DP+滚动数组优化
问题描述 LG3004 题解 把拿走的过程反向,看做添加的过程,于是很显然的区间DP模型. 设\(opt_{i,j}\)代表区间\([i,j]\)中Bessie可以获得的最大值,显然有 \[opt_{ ...
- CodeForces 173C Spiral Maximum 记忆化搜索 滚动数组优化
Spiral Maximum 题目连接: http://codeforces.com/problemset/problem/173/C Description Let's consider a k × ...
- 51nod 编辑距离 + 滚动数组优化
这道题一开始觉得增加和删除会移动字符串的位置很不好做 两个字符串dp状态一般是第一个前i个和第二个前j个 #include<cstdio> #include<algorithm> ...
- 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段,然后根据 ...
- poj1159 dp(滚动数组优化)
H - 简单dp 例题扩展 Crawling in process... Crawling failed Time Limit:3000MS Memory Limit:65536KB ...
随机推荐
- T3137 栈练习1 codevs
codevs.cn/problem/3137 题目描述 Description 给定一个栈(初始为空,元素类型为整数,且小于等于100),只有两个操作:入栈和出栈.先给出这些操作,请输出最终栈的栈顶元 ...
- T3186 队列练习2 codevs
http://codevs.cn/problem/3186/ 题目描述 Description (此题与队列练习1相比改了2处:1加强了数据 2不保证队空时不会出队)给定一个队列(初始为空),只有两种 ...
- 【深入Java虚拟机】之三:类初始化
类初始化是类加载过程的最后一个阶段,到初始化阶段,才真正开始执行类中的Java程序代码.虚拟机规范严格规定了有且只有四种情况必须立即对类进行初始化: 遇到new.getstatic.putstatic ...
- Swift标识符和keyword
不论什么一种计算机语言都离不开标识符和keyword,下面我们将具体介绍Swift标识符和keyword. 标示符 标识符就是给变量.常量.方法.函数.枚举.结构体.类.协议等指定的名字.构成标识符的 ...
- python各种类型转换
python各种类型转换 学习了:https://blog.csdn.net/shanliangliuxing/article/details/7920400 https://blog.csdn.ne ...
- CVE-2014-4114 和 CVE-2014-3566
这两天关注安全的人员都会特别留意这两个新披露的漏洞:CVE-2014-4114 和 CVE-2014-3566.以下我们就针对这两个漏洞最一些简要说明. CVE-2014-4114------- ...
- C语言使用以及其它语言经常出的错误
1. 计算优先级 i/*pi/(*p) //默认即用“()”花括号分隔即可 2.分号结束 ; ; if(i<j);{ //code } //分号结束必须看清楚 3.声明变量 ; ; //声明变量 ...
- react jsx 数组变量的写法
1.通过 map 方法 var students = ["张三然","李慧思","赵思然","孙力气","王萌 ...
- 多线程网页爬虫 python 实现
采用了多线程和锁机制,实现了广度优先算法的网页爬虫. 对于一个网络爬虫,如果要按广度遍历的方式下载,它就是这样干活的: 1.从给定的入口网址把第一个网页下载下来 2.从 ...
- php 封装memcache类
<?php /* * memcache类 */ class Memcacheds{ //声明静态成员变量 private static $m = null; pri ...