问题如下
考虑下面显示的数字三角形。编写一个程序,计算可以在从顶部开始到底部某处的路径上传递的最大数字总和。每个步骤都可以向左斜下方或向右斜下方。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的示例中,路由7 -> 3 -> 8 -> 7 -> 5
产生最高总和:30。
我遇到以下错误
Execution error: Your program (`numtri') used more than the
allotted runtime of 1 seconds (it ended or was stopped at 1.674
seconds) when presented with test case 6. It used 6080 KB of
memory.
我的程序适用于输入< = 8三角形大小。但是,当三角形大小超过8时,它就失败了。 为什么会这样,我不知道。请帮忙。
这是我的代码:
#define MAX 1000
int max=0,a[MAX][MAX];
void dfs(int i,int j,int end,int sum)
{
if(i<=end)
{
sum += a[i][j];
dfs(i+1,j,end,sum);
dfs(i+1,j+1,end,sum);
}
else
{
if(sum>max)
max = sum;
}
}
int main () {
FILE *fin = fopen ("numtri.in", "r");
FILE *fout = fopen ("numtri.out", "w");
int r,i,j;
fscanf(fin,"%d",&r);
for(i = 1;i<=r;i++)
for(j = 1;j<=i;j++)
fscanf(fin,"%d",&a[i][j]);
dfs(1,1,r,0);
fprintf(fout,"%d\n",max);
fclose(fin);
fclose(fout);
return 0;
}
它适用于前5个测试用例,但在第6个失败时有199个三角形大小。
答案 0 :(得分:2)
每当您的程序遇到金字塔中的特定点时,它会计算到底部的最佳路径。但是,您可以观察到每个点遇到不止一次,因此计算出最佳路径数次。因此,您的程序将以指数时间运行。
如果您改为保存三角形中某个点(此处为dp[i][j]
)可实现的最大总和,并重新使用该值而不是重新计算它,一旦再次点击该点,您的程序将会快得多。那是因为该算法只访问金字塔中的每个点一次。这称为自上而下的动态编程。
#include<string.h>
#include<stdio.h>
#define MAX_N 1005
int a[MAX_N][MAX_N];
int dp[MAX_N][MAX_N];
int max(int a, int b)
{
return a > b ? a : b;
}
int dfs(int i,int j,int end)
{
if(dp[i][j] != -1)
{
return dp[i][j];
}
else if(i <= end)
{
return dp[i][j] = a[i][j] + max(dfs(i+1,j,end), dfs(i+1,j+1,end));
}
else
{
return 0;
}
}
int main () {
FILE *fin = fopen ("numtri.in", "r");
FILE *fout = fopen ("numtri.out", "w");
int r,i,j;
memset(dp, -1, sizeof dp);
fscanf(fin,"%d",&r);
for(i = 1;i<=r;i++)
for(j = 1;j<=i;j++)
fscanf(fin,"%d",&a[i][j]);
fprintf(fout,"%d\n", dfs(1,1,r));
fclose(fin);
fclose(fout);
return 0;
}
答案 1 :(得分:1)
使用DFS解决此问题的效率很低,原因如下:考虑一条路径先行,然后左转,另一条路径先左转,然后右转。这些路径现在位于同一位置,从此位置引出的路径将计算两次。在金字塔的较低层次,情况更糟,给出了指数运行时间。
您需要做的是动态编程。我们使用这个问题展示最优子结构(对于最大路径必须是最大的所有子路径)和重叠子问题(上述行为)的事实。这使我们可以避免不必要的工作。
有两种可能的方法。
自上而下通过记忆:执行dfs,但在返回时保存给定单元格的计算值。这样当您再次访问一个单元格时,您不必从该单元格执行dfs,并且可以立即返回。
自下而上:从底行开始,并保留从当前行中的每个单元格开始可实现的最大总和的列表。首先,这只是底部的数字。然后,对于下一行,第i行的单元格j将具有最大总和:a[i][j] + max(maxsum[i+1][j], maxsum[i+1][j+1])
有关更多信息,请阅读维基百科或您最喜欢的算法书中的动态编程。
答案 2 :(得分:0)
dfs()
例程中的递归赋予dfs
一个在搜索深度呈指数级的运行时,其中大部分工作都是冗余的。
对于这个问题,有一个简单的非递归解决方案。而不是递归到dfs
,维护当前级别路径最大值的向量v
。要更深入一级,请将v
复制到工作向量w
;然后将每个元素v[j]
设置为a[i][j]
加上较大的w[j]
和w[j+1]
。
答案 3 :(得分:0)
BFS可以代替dp来使用,因为遇到访问的节点时,您可以简单地更改其值,因为直到图的末尾才遍历,但必须非常注意内存。这是我在USACO平地机上正常工作的代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef vector<long long> vi;
typedef pair<long long,long long> pi;
typedef vector<pi> vpi;
#define FOR(i, a, b) for(ll i=ll(a); i<ll(b); i++)
#define ROF(i, a, b) for(ll i=ll(a); i>=ll(b); i--)
#define f first
#define s second
#define pb emplace_back
#define mp make_pair
#define SQ(a) (a)*(a)
#define all(a) (a).begin(), (a).end()
int main() {
ifstream cin("numtri.in");
ofstream cout("numtri.out");
int n;
int adjacency_list[500500];
int matrix[1005][1005];
bool visited[500500];
int value[500500];
int total[500500];
int maxnum=0;
cin>>n;
int s=0,x=0;
for(int i=0;i<n;i++){
s++;
for(int j=0;j<s;j++){
cin>>value[x];
total[x]=0;
matrix[i][j]=x;
x++;
}
}
s=0;
for(int i=0;i<n-1;i++){
s++;
for(int j=0;j<s;j++){
adjacency_list[matrix[i][j]]=(matrix[i+1][j]);
}
}
total[0]=value[0];
queue<int>q;
q.push(0);
while(!q.empty()){
int current=q.front();
q.pop();
if(current<((n*(n+1) )/2)-1-(n-1 ) ){
int i =adjacency_list[current];
if(!visited[i]){
q.push(i);
total[i]=total[current]+value[i];
//cout<<total[i];
}
else{
if(total[current]+value[i]>total[i]){
total[i]=total[current]+value[i];
}
}
maxnum=max(total[i],maxnum);
visited[i]=1;
i =adjacency_list[current]+1;
if(!visited[i]){
q.push(i);
total[i]=total[current]+value[i];
}
else{
if(total[current]+value[i]>total[i]){
total[i]=total[current]+value[i];
}
}
maxnum=max(total[i],maxnum);
visited[i]=1;
}
}
cout<<maxnum<<endl;
return 0;
}