题意
比最普通的数字三角形题目多加了一个限制 就是竖直向下的步数和向右下的步数之差<=1
思路
1<=N<=100
一开始dfs 只能通过50%的样例
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define ar2 array<int,2>
#define ar3 array<int,3>
#define ar4 array<int,4>
#define endl '\n'
void cmax(int &a,int b){a=max(a,b);}
void cmin(int &a,int b){a=min(a,b);}
const int N=200,MOD=1e9+7,INF=0x3f3f3f3f,LINF=LLONG_MAX;
int n,ans=0;
int g[N][N],dp[N][N];
bool vis[N][N];
void dfs(int layer,int idx,int l,int r,int sum){//layer上一次考虑第几层 idx表示第layer层选的是第idx个
if((n-1)&1){
if(max(l,r)>(n-1)/2+1){
return;
}
}else{
if(max(l,r)>(n-1)/2){
return;
}
}
if(layer==n){
if(abs(l-r)<=1)
cmax(ans,sum);
return;
}
dfs(layer+1,idx,l+1,r,sum+g[layer+1][idx]);
dfs(layer+1,idx+1,l,r+1,sum+g[layer+1][idx+1]);
}
void solve() {
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>g[i][j];
}
}
dfs(1,1,0,0,g[1][1]);
cout<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t=1;
// cin>>t;
while (t--) solve();
}
看了题解 只需要简单的dp
观察发现 竖直向下和向右下走的步数要么相等 要么差一步
在列方向上 只有向右下走的步数会对最终答案所在的列产生影响
一共n行 要走n-1步
①n为奇数 n-1为偶数 向下和向右各走(n-1)/2步 所以最终答案落在dp[n][(n-1)/2+1]
②n为偶数 n-1为奇数 向右走(n-1)/2或者(n-1)/2+1步
所以最终答案对dp[n][(n-1)/2+1]和dp[n][(n-1)/2+2]取max即可
这里必须对n分类讨论 如果n为奇数 但是还用②取max的方法会wa
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define ar2 array<int,2>
#define ar3 array<int,3>
#define ar4 array<int,4>
#define endl '\n'
void cmax(int &a,int b){a=max(a,b);}
void cmin(int &a,int b){a=min(a,b);}
const int N=200,MOD=1e9+7,INF=0x3f3f3f3f,LINF=LLONG_MAX;
int n;
int g[N][N],dp[N][N];
void solve() {
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>g[i][j];
}
}
dp[1][1]=g[1][1];
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i][j]=g[i][j]+max(dp[i-1][j],dp[i-1][j-1]);
}
}
if(n&1) cout<<dp[n][(n-1)/2+1]<<endl;
else cout<<max(dp[n][(n-1)/2+1],dp[n][(n-1)/2+2])<<endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t=1;
// cin>>t;
while (t--) solve();
}
DFS记忆化剪枝
int dp[N][N][N]; // 三维,(layer, idx, l)
void dfs(int layer,int idx,int l,int r,int sum){
if((n-1)&1){
if(max(l,r)>(n-1)/2+1) return;
}else{
if(max(l,r)>(n-1)/2) return;
}
if(layer==n){
if(abs(l-r)<=1) cmax(ans,sum);
return;
}
if(dp[layer][idx][l]>=sum) return; // 记忆化剪枝
dp[layer][idx][l]=sum;
dfs(layer+1,idx,l+1,r,sum+g[layer+1][idx]);
dfs(layer+1,idx+1,l,r+1,sum+g[layer+1][idx+1]);
}
void solve(){
memset(dp,-1,sizeof dp);
......
}
GPT帮改的记忆化 其实只加了两行代码 感觉茅塞顿开
别忘了初始化dp数组为-1
这样就能ac了
如何确定状态?
你的原递归函数是这样的:
void dfs(int layer, int idx, int l, int r, int sum)
layer
: 当前在第几层。idx
: 当前层的第几个位置。l
: 向左下方向走的次数。r
: 向右下方向走的次数。sum
: 到当前为止路径上累积的和。
分析状态冗余:
sum
不需要纳入记忆化状态,因为记忆化的目的是剪枝,而不是精确存储所有的路径之和 sum就是dp数组对应的值 不需要开一维记录这个状态r
可以用其他变量代替:
因为在第layer
层时,已经走过的步数 = layer - 1。
所以:
这样一来,r = (layer - 1) - l;
r
就不用存储了,因为它可用其他变量计算出来。
因此真正关键的状态只有三个:
layer
(第几层)idx
(当前所在的第几个位置)l
(向左下走了几次)
只要这三个状态相同,那么剩余的路径情况就完全一致
也就是说 这三个状态就能唯一确定一个dp值
所以记忆化数组优化为三维:
dp[layer][idx][l]
如何用dp数组剪枝?
如果当前是搜索到的更大 那么更新dp值 并继续往下搜
if (dp[layer][idx][l] >= sum) return;
dp[layer][idx][l] = sum;
GPT写的记忆化剪枝模板
int dp[层数][位置数][其他状态参数];
void dfs(状态参数们, int sum) {
if (到达终点或满足条件) {
更新答案;
return;
}
if (dp[当前状态] >= sum) return; // 记忆化剪枝
dp[当前状态] = sum; // 更新最佳状态值
dfs(下一个状态, 更新的sum);
dfs(另一个状态, 更新的sum);
}