题目大意
给你一个
n
×
n
n \times n
n×n 的矩阵,每个点有两个值,代表当前点物品的累加的数量和累加的价值,累加的价值就是说你到达了这个点,你所有物品的价值都会累加上这个价值,累加的数量表示你到达了这个点你的物品数量会加上这个值,你每次只能向下或者向右走。问你从
(
1
,
1
)
(1, 1)
(1,1) 走到
(
n
,
n
)
(n, n)
(n,n) 所获得的总价值最大是多少,总价值为数量乘以价值。
1
≤
n
≤
100
1 \leq n \leq 100
1≤n≤100
解题思路
d
p
dp
dp
由于每个点有两个状态,我们将每个点的状态用一个
v
e
c
t
o
r
vector
vector 存起来。
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
j
]
+
d
p
[
i
]
[
j
−
1
]
dp[i][j] = dp[i-1][j] + dp[i][j-1]
dp[i][j]=dp[i−1][j]+dp[i][j−1]
此时,走到
i
,
j
i, j
i,j 位置的答案就是
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 里面每个点的二维乘积
但是这样的复杂度是
O
(
T
×
2
n
−
1
)
O(T \times 2^{n-1})
O(T×2n−1)
我们发现有一些状态是必不可能成为答案的,我们需要将这些状态给即使排除。比如
f
i
i
≤
f
i
j
&
&
s
e
i
≤
s
e
j
fi_i \leq fi_j \ \&\& \ se_i \leq se_j
fii≤fij && sei≤sej
那么,
i
i
i 这个点永远没有
j
j
j 这个点优,所以我们要即使排除这个状态,以减少复杂度。
每次我们在合并的时候归并一下,把每个点的
v
e
c
t
o
r
vector
vector 当做一个单调栈,当出现上述情况时及时排除不可能的答案。
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n;
ll a[107][107];
ll b[107][107];
vector<PLL> v[107][107];
void solve(){
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> a[i][j];
}
}
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= n; ++j){
cin >> b[i][j];
}
}
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= n; ++j){
if(i == 1 && j == 1){
v[i][j].pb({a[i][j], b[i][j]});
continue;
}
ll num = a[i][j];
ll add = b[i][j];
int p1 = 0;
int sz1 = v[i-1][j].size();
int p2 = 0;
int sz2 = v[i][j-1].size();
int cnt = -1;
while(p1 < sz1 && p2 < sz2){
int f1 = v[i-1][j][p1].fi;
int s1 = v[i-1][j][p1].se;
int f2 = v[i][j-1][p2].fi;
int s2 = v[i][j-1][p2].se;
if(f1 <= f2){
while(cnt != -1 && v[i][j][cnt].fi <= f1 + num && v[i][j][cnt].se <= s1 + add){
cnt--;
v[i][j].pop_back();
}
v[i][j].pb({f1 + num, s1 + add});
cnt++;
p1++;
}
else{
while(cnt != -1 && v[i][j][cnt].fi <= f2 + num && v[i][j][cnt].se <= s2 + add){
cnt--;
v[i][j].pop_back();
}
cnt++;
v[i][j].pb({f2 + num, s2 + add});
p2++;
}
}
while(p1 < sz1){
int f1 = v[i-1][j][p1].fi;
int s1 = v[i-1][j][p1].se;
while(cnt != -1 && v[i][j][cnt].fi <= f1 + num && v[i][j][cnt].se <= s1 + add){
cnt--;
v[i][j].pop_back();
}
v[i][j].pb({f1 + num, s1 + add});
cnt++;
p1++;
}
while(p2 < sz2){
int f2 = v[i][j-1][p2].fi;
int s2 = v[i][j-1][p2].se;
while(cnt != -1 && v[i][j][cnt].fi <= f2 + num && v[i][j][cnt].se <= s2 + add){
cnt--;
v[i][j].pop_back();
}
cnt++;
v[i][j].pb({f2 + num, s2 + add});
p2++;
}
}
}
ll ans = 0;
for(auto [x, y] : v[n][n]){
ans = max(ans, x * y);
}
cout << ans << "\n";
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= n; ++j){
v[i][j].clear();
}
}
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
qc;
int T;
cin >> T;
// T = 1;
while(T--){
solve();
}
return 0;
}