[DP 区间 高精]矩阵取数
题目
思路
可以发现每行独立,所以单独考虑每行的max
定义
f
[
i
]
[
j
]
f[i][j]
f[i][j]为从i到j进行取数得到的最大值
首先我们不考虑权值
2
i
,
可
以
得
到
以
下
状
态
转
移
2^i,可以得到以下状态转移
2i,可以得到以下状态转移
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
+
1
]
[
j
]
+
w
[
i
]
,
f
[
i
]
[
j
−
1
]
+
w
[
r
]
)
f[i][j]=max(f[i+1][j]+w[i],f[i][j-1]+w[r])
f[i][j]=max(f[i+1][j]+w[i],f[i][j−1]+w[r])
然后考虑权值,我们用小数据举例
f
[
1
]
[
1
]
=
2
∗
g
[
1
]
,
f
[
2
]
[
2
]
=
2
∗
g
[
2
]
,
f
[
3
]
[
3
]
=
2
∗
g
[
3
]
f[1][1]=2*g[1],f[2][2]=2*g[2],f[3][3]=2*g[3]
f[1][1]=2∗g[1],f[2][2]=2∗g[2],f[3][3]=2∗g[3]
f
[
1
]
[
2
]
=
m
a
x
(
4
∗
g
[
1
]
+
2
∗
g
[
2
]
,
2
∗
g
[
1
]
+
4
∗
g
[
2
]
)
=
m
a
x
(
2
∗
f
[
1
]
+
2
∗
g
[
2
]
,
2
∗
f
[
2
]
[
2
]
+
2
∗
g
[
2
]
)
f[1][2]=max(4*g[1]+2*g[2],2*g[1]+4*g[2])=max(2*f[1]+2*g[2],2*f[2][2]+2*g[2])
f[1][2]=max(4∗g[1]+2∗g[2],2∗g[1]+4∗g[2])=max(2∗f[1]+2∗g[2],2∗f[2][2]+2∗g[2])
f
[
l
]
[
r
]
=
m
a
x
(
f
[
l
+
1
]
[
r
]
∗
2
+
2
∗
g
[
l
]
,
f
[
l
]
[
r
−
1
]
∗
2
+
2
∗
g
[
r
]
)
f[l][r]=max(f[l+1][r]*2+2*g[l],f[l][r-1]*2+2*g[r])
f[l][r]=max(f[l+1][r]∗2+2∗g[l],f[l][r−1]∗2+2∗g[r])
写dp的话需要牢记的一点是,如果是for循环迭代的形式,思路应该是反过来的,应该是从等号右边转移到左边应该怎么样。
递归写法状态转移
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
+
1
]
[
j
]
+
g
[
i
]
∗
2
k
,
f
[
i
]
[
j
−
1
]
+
g
[
j
]
∗
2
k
)
f[i][j]=max(f[i+1][j]+g[i]*2^k,f[i][j-1]+g[j]*2^k)
f[i][j]=max(f[i+1][j]+g[i]∗2k,f[i][j−1]+g[j]∗2k)
代码
// Problem: P1005 [NOIP2007 提高组] 矩阵取数游戏
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1005
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// FishingRod
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
inline __int128 read(){
__int128 x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void print(__int128 x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
print(x / 10);
putchar(x % 10 + '0');
}
//#define MULINPUT
/*DATA & KEY
*/
int T;
const int N=100;
LL g[N][N];
__int128 f[N][N][N];
__int128 ans;
LL fp(LL a,LL b){
LL res=1;
while(b){
if(b&1)res=res*a;
b>>=1;
a=a*a;
}
return res;
}
void solve(int C)
{
//NEW DATA CLEAN
//NOTE!!!
LL n,m;cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
for(int i=1;i<=n;i++){
for(int len=1;len<=m;len++){
for(int l=1;l+len-1<=m;l++){
int r=l+len-1;
f[i][l][r]=max(2*f[i][l+1][r]+2*g[i][l],2*f[i][l][r-1]+2*g[i][r]);
}
}
}
for(int i=1;i<=n;i++)ans+=f[i][1][m];
print(ans);
}
int main()
{
#ifdef MULINPUT
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
#else
solve(1);
#endif
return 0;
}
递归+记忆化写法
// Problem: P1005 [NOIP2007 提高组] 矩阵取数游戏
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1005
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// FishingRod
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
inline __int128 read(){
__int128 x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
inline void print(__int128 x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
print(x / 10);
putchar(x % 10 + '0');
}
//#define MULINPUT
/*DATA & KEY
*/
int T;
const int N=100;
LL g[N][N];
__int128 f[N][N][N];
__int128 q[N];
__int128 n,m,ans;
__int128 dfs(int i,int l,int r){
if(f[i][l][r])return f[i][l][r];
if(l==r)return g[i][l]*((__int128)1<<m);
__int128 k=m-(r-l);
return f[i][l][r]=max(dfs(i,l+1,r)+((__int128)1<<k)*g[i][l],dfs(i,l,r-1)+((__int128)1<<k)*g[i][r]);
}
void solve(int C)
{
//NEW DATA CLEAN
//NOTE!!!
n=read(),m=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>g[i][j];
for(int i=1;i<=n;i++)ans+=dfs(i,1,m);
print(ans);
}
int main()
{
#ifdef MULINPUT
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
#else
solve(1);
#endif
return 0;
}