牛客小白月赛 72 D
题目链接
链接:https://ac.nowcoder.com/acm/contest/56825/D
来源:牛客网
示例1
输入
3 3
1 2 3
4 5 6
7 8 9
2
2
1 1
3 3
3
1 1
1 3
3 1
输出
58
41
题解:
如果先不考虑传送门,这题就是一道简单dp
设状态 d p [ i ] [ j ] dp[i][j] dp[i][j]表示到 i , j i,j i,j这个点的能获得最大的宝藏和
那么题目给出只能往下或者往右走
状态转移方程就变成了 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] + v a l [ i ] [ j ] , d p [ i ] [ j − 1 ] + v a l [ i ] [ j ] ) dp[i][j]=max(dp[i-1][j]+val[i][j],dp[i][j-1]+val[i][j]) dp[i][j]=max(dp[i−1][j]+val[i][j],dp[i][j−1]+val[i][j])
但要考虑传送门就复杂一些了
当时做的时候就是想不到传送门怎么解决,后续题解在下面
当时代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct node{
int x,y;
};
const int N = 1e3+1;
int n,m;
int a[N][N];
int dp[N][N];
int all=0;
void dfs(int x,int y){
//no
nullptr;
}
int vis[5];
void sove(void){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
scanf("%lld",&a[i][j]);
}
int t;scanf("%lld",&t);
while(t--){
int k;scanf("%lld",&k);
node men[k];
for(int i=0;i<k;i++)scanf("%lld %lld",&men[i].x,&men[i].y);
all=0;
//dfs(1,1);
//dp
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[i][j]=max(dp[i-1][j]+a[i][j],dp[i][j-1]+a[i][j]);
}
}
all=max(all,dp[n][m]);
for(int o=0;o<k;o++){
int o2=k-o;
if(vis[o2]==true)continue;
vis[o2]=vis[o]=true;
dp[men[o].x][men[o].y]=
for(int i=men[o].x;i<=n;i++)
{
for(int j=men[o].y;j<=m;j++)
if(i>=men[o].x+1&&j>=men[o].y+1)
dp[i][j]=max(dp[i-1][j]+a[i][j],dp[i][j-1]+a[i][j]);
else{
if(i==men[o].x&&j>=men[o].y+1)dp[i][j]=dp[i-1][j]+a[i][j];
if(j==men[o].y&&i>=men[o].x+1)dp[i][j]=dp[i][j-1]+a[i][j];
}
}
all=max(all,dp[n][m]);
}
//all=dp[n][m];
cout<<all<<endl;
}
}
signed main(void){
int _=1;
//cin>>_;
while(_--){
sove();
}
return 0;
}
题解续:
有传送门的话,我们就可以从后面的传送门传送到前面去,再拿一次宝藏
但其实题目说了,只能使用一次传送门,那么题目就没那么复杂了。
还是先来一个dp,依旧是同上述一样计算,这个算出来的 d p [ n ] [ m ] dp[n][m] dp[n][m]其实就是不用传送门的走法。
那么再反过来思考,如果在 i 1 , j 1 i_1,j_1 i1,j1处使用了传送门传送到 i 2 , j 2 i_2,j_2 i2,j2,其实就算 ( 1 , 1 ) (1,1) (1,1) 到 ( i 1 , j 1 ) (i_1,j_1) (i1,j1)和从 ( i 2 , j 2 ) (i_2,j_2) (i2,j2)到 ( n , m ) (n,m) (n,m)的宝藏价值和。
所以只需要加一次反向dp,就可以计算从 ( i 2 , j 2 ) (i_2,j_2) (i2,j2)到 ( n , m ) (n,m) (n,m)的最大宝藏价值
#include <bits/stdc++.h>
#define int long long
using namespace std;
struct node{
int x,y;
};
const int N = 2*1e3+10;
int n,m;
int a[N][N];
int dp[N][N];
int dp2[N][N];
int all=0;
void dfs(int x,int y){
//no
nullptr;
}
int vis[5];
void sove(void){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
scanf("%lld",&a[i][j]);
}
//dp
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i==1)dp[i][j]=dp[i][j-1]+a[i][j];
else if(j==1)dp[i][j]=dp[i-1][j]+a[i][j];
else
dp[i][j]=max(dp[i-1][j]+a[i][j],dp[i][j-1]+a[i][j]);
}
}
for(int i=n;i>=1;i--){
for(int j=m;j>=1;j--){
if(i==n)dp2[i][j]=dp2[i][j+1]+a[i][j];
else if(j==m)dp2[i][j]=dp2[i+1][j]+a[i][j];
else
dp2[i][j]=max(dp2[i+1][j]+a[i][j],dp2[i][j+1]+a[i][j]);
}
}
int t;scanf("%lld",&t);
while(t--){
int k;scanf("%lld",&k);
node men[k];
for(int i=0;i<k;i++)scanf("%lld %lld",&men[i].x,&men[i].y);
all=0;
//dfs(1,1);
all=max(all,dp[n][m]);
for(int i=0;i<k;i++){
for(int j=0;j<k;j++){
if(i==j)continue;
all=max(all,dp[men[i].x][men[i].y]+dp2[men[j].x][men[j].y]);
}
}
//all=dp[n][m];
cout<<all<<endl;
}
}
signed main(void){
int _=1;
//cin>>_;
while(_--){
sove();
}
return 0;
}