寻找能够提供最小能量的路径

3

ACM国际大学生程序设计竞赛,亚洲-Amritapuri站,2011年 问题A:MAGRID

非常感谢您在十月份帮助哈利波特找到不朽的魔法石。我们没有告诉你那只是一个在线游戏吗?现在这里是真正的任务。你有一个magrid S(一个魔法网格),它有R行和C列。此magrid中的每个单元格都有匈牙利角蜥蜴,我们勇敢的英雄必须打败它,或者是他的老师斯内普留给他的一瓶魔法药水。单元格(i,j)处的龙会从哈利身上取走|S [i] [j]|的力量点数,而单元格(i,j)处的药水会增加哈利的力量S [i] [j]。如果哈利在旅途中的任何时刻力量降至0或更低,他就会死亡,没有魔法石可以使他复活。

哈利从左上角单元格(1,1)开始,而魔法石位于右下角单元格(R,C)。从单元格(i,j)开始,哈利只能向下或向右移动一个单元格,即到达单元格(i + 1,j)或单元格(i,j + 1),他不能移动到 magrid之外。哈利在开始旅程之前使用魔法确定了哪个单元格包含什么,但缺乏基本的简单数学技能来确定他需要从哪里开始具有收集魔法石的最小强度。请再次帮助他。

输入(STDIN):

第一行包含测试用例T的数量。 T个测试用例。 每个测试用例都由第一行中的R C组成,后跟描述R行的网格, 每行包含C个整数。从上到下,行编号为1到R,从左到右,列编号为1到C。 S [i] [j]<0的单元格包含龙,其他单元格包含魔法药水。

输出(STDOUT):

输出T行,每个案例一个,包含哈利应该从单元格(1,1)开始具有正力量的最小强度,以便在旅途中到达单元格(R,C)。

约束条件:

1 ≤ T ≤ 30
2 ≤ R, C ≤ 500
-103 ≤ S[i][j] ≤ 103
S[1][1] = S[R][C] = 0

时间限制:3秒 内存限制:64 MB 样例输入:

3
2 3
0 1 -3
1 -2 0
2 2
0 1
2 0
3 4
0 -2 -3 1
-1 4 0 -2
1 -2 -3 0

示例输出:

2
1
2

说明: 情况1:如果Harry在(1,1)单元格以力量=1开始,他无法在任何可能的路径中保持正能量。他需要最初的力量至少为2。 情况2:请注意,要从(1,1)开始,他至少需要力量=1。
我尝试了我的第一种方法,查看所有路径并选择具有最小能量的路径。
#include<iostream>
#include<algorithm>
#include<stack>
#include<cmath>
using namespace std;

int TT,R,C,S[500][500];
int energy_g;
//unsigned long int fact(int a);
int trace(int r,int c,int energy,int energy_r);
int main(void)
{

    cin>>TT;

    for(int i=1;i<=TT;i++)
    {
    cin>>R>>C;     
    for(int r=1;r<=R;r++)
         for(int c=1;c<=C;c++)
         {
             cin>>S[r][c];
         //cout<<S[r][c];   
         }
     energy_g=32000;
     trace(1,1,0,0);
     cout<<energy_g<<endl;
    }
    return 0;
}

int trace(int r,int c,int energy,int energy_r)
{
    if(r>R || c>C)
        return 0;
    energy += S[r][c];
    if(energy < 0)
    {
    energy_r+=abs(energy)+1 ;       
    energy+=abs(energy)+1;
    }


    else if(energy == 0){        
    energy_r +=1;   
    energy +=1;
     }

    if(r == R && c == C)
    {

    if(energy_r < energy_g)
            energy_g = energy_r;
        return 0;
    }
    trace(r,c+1,energy,energy_r);
    trace(r+1,c,energy,energy_r);
    return 0;
}

请帮助我进一步优化它,因为我知道我实现的方法需要最长时间。


2
提示:倒着工作,每次计算从某个位置(i,j)到达终点所需的能量时,请记住它,以便您不必重新计算 :) - j_random_hacker
1个回答

2
这是我脑海中的一个解决方案。我们将使用相当常见的技巧 - 二分查找答案。解决方案可以分为两个部分:
1)检查我们是否可以在健康水平为N的情况下完成旅程。这可以通过简单的动态规划来实现 - 让dp [i] [j]成为当我们移动到单元格(i,j)时我们可以拥有的最大能量级别。由于我们只能向下或向右移动,因此可以将dp [i] [j]归结为从(i-1,j)和(i,j-1)移动中的最佳移动。如果级别降至1以下,请将值设置为负无穷大,因为我们不能让Harry死亡:)(或在执行DP步骤时添加排除不可能的瓷砖的检查)。 dp [0] [0]是N,可以直接计算dp [i] [0],dp [0] [i]。然后继续进行类似表格的填充。为了可视化,请参见http://www.cs.berkeley.edu/~vazirani/algorithms/chap6.pdf的图6.4。
2)对答案进行二分查找。最初left = 1,right = 2000000000(或其他适当的上限) - 这些是二分查找的左右边界。在每次二分查找迭代中,我们检查是否可以使用mid =(left + right)/ 2的健康水平完成旅程,并递归到适当的一半。
这在实践中效果很好 - 每个DP步骤需要O(R * C)时间,并且二分查找添加了一个约为30的log(2000000000)因子。这应该没问题。
也许只能用DP解决,但我现在看不出来。

1
不需要二分搜索。根据@j_random_hacker的提示,您可以直接从右到左计算底行每个单元格所需的最小能量。对于最右侧的列,同样是从下到上开始计算。一旦计算出它们的底部和右侧邻居,其他单元格就可以计算出来。 - NonNumeric
这绝对是二分查找略微低效的。 - Tushar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接