tyvj p1264 艾泽拉斯的激流
描述
沿着卡利姆多北方边界延展的破碎的海岸,在世界大分裂之前曾经是暗夜精灵首都艾萨琳的一部分,艾萨拉。恶魔从这个世界被消除,这片土地被撕碎并被大海吞没,剩下的只有曾经雄伟城市的废墟。自那以后,这个岩石交错的岛屿、峭壁悬崖和珊瑚丛生的海洋成为许多传说来源。暗夜精灵们认为这是个被诅咒的地方,连经验最丰富的船长都从这里绕行。这里有巨大的水生物,可怕的暗礁,强大的激流与巨浪。然而传闻这里水下有着惊人的宝藏,吸引了一支矮人探险队前来。矮人们经过多日的探险,终于发现了在一个水域下面有着未知的宝藏。但是这个海域常常有着激流与巨浪,矮人们不可能把探险艇停下来甚至逆流而上。
这个海域可以被描述为一个W×L的矩形,分成个一个个单元格。每个单元格代表的海域下面可能是宝藏,也可能是一块可怕地礁石。从激流的上游开始,每过1秒探险艇就会被冲往下游的一个单位。在被冲往下游的过程中,矮人们可以控制方向,选择他的正前,左前边,或右前边的一个单位。矮人们决定从这个激流的最上游的任意一个单元格开始向下漂流,每经过一个单元格就可以取走这个单元格上的宝藏。但是千万不能碰到礁石,否则他的探险艇会支離破碎。请你安排一个计划,算出探险队最多一共能拿到多少宝藏。
输入格式
第1行,两个整数W,L。
接下来的L行,每行W个整数,以“从上游到下游,面朝水流方向从左向右”的顺序依次为每个单元格中的宝藏的数目,如果为-1则表示这个单元格是礁石。
输出格式
一个整数,表示得到的宝藏。
测试样例1
输入
3 5
5 1 3
-1 7 -1
5 1 10
4 -1 7
20 10 5
3 5
5 1 3
-1 7 -1
5 1 10
4 -1 7
20 10 5
输出
41
41
备注
上游->下游1 2 3 4 5
1 5 -1 5 4 20
2 1 7 1 -1 10
3 3 -1 10 7 5
如上表,探险队从(1,1)开始,第1秒向右转一下,被冲到(2,2)。第2秒向左转一下,被冲到(3,1)。接下来第3秒正前行走,经过(4,1),(5,1),一共拿到5+7+5+4+20=41个单位的宝藏。
数据规模
1<=W<=1000
1<=L<=8000
所有涉及到的数字不会超过32位带符号整型的范围
根据题意,可以轻易想到用搜索或者动态规划来解决。考虑到数据规模,搜索应该使用记忆化搜索。这里介绍逆推动态规划算法。
考虑是从第一行任意位置出发,所以最终状态是max(f[1][1..w])。
由于只能向前、左前、右前移动,所以每一个点的状态由其下方、左下方、右下方转移过来,所以动态转移方程为:f[i][j]=max(f[i+1][j],f[i+1][j-1],f[i+1][j+1])+s[i][j],其中需要考虑边缘的状态。由于题目中说有障碍,所以将障碍点赋值为-maxlongint即可。
边缘直接赋值为0即可。
<span style="font-size:14px;">#include<iostream> #include<cstring> #include<cmath> using namespace std; long n,m; long s[8003][1004]; long f[8003][1004]; inline long max(long a,long b,long c) {long h; h=(a>b)? a:b; h=(h>c)? h:c; return h;} int main() {cin>>n>>m; long i,j,k; memset(s,128,sizeof(s)); memset(f,-11,sizeof(f)); for(i=1;i<=m;i++) {for(j=1;j<=n;j++){cin>>s[i][j]; if(s[i][j]==-1) s[i][j]=-0x77777777;}} for(i=1;i<=n;i++) f[m][i]=s[m][i]; for(i=(m-1);i>=1;i--) {for(j=1;j<=n;j++){f[i][j]=max(f[i+1][j],f[i+1][j-1],f[i+1][j+1])+s[i][j];}} j=0; for(i=1;i<=n;i++) j=(j>f[1][i])? j:f[1][i]; cout<<j<<endl; return 0;}</span>