题目链接
https://www.luogu.com.cn/problem/P1004
题意
n阶方阵,每个格子有值,从左上走到右下两次,不能取走同一格子的值两次,求取到的值的最大总和
思路
三维dp同时维护两个状态,dp[k][i1][i2]代表总共走了k-2步(为了编码方便),第一次处于i1行,第二次处于i2行时能取的最大值。
因为步数固定了,所以列号也固定了,考虑转移方程,一共四个方向可以走到当前情况:第一次向右,第二次向下;第一次向下,第二次向右;第一次向下,第二次向下;第一次向右,第二次向右;所以四种状态取最大值即可。
再考虑如何处理不重复的问题,因为不可能走回头路, 所以我们只需要处理每走一步时两次不要取同一个数就可以了,dp第一维为k时,当且仅当i1==i2时到了同一个格子,这样的话只要把当前状态加上w[i1][k-i1]即可,若不相等,则需要加上w[i1][k-i1]和w[i2][k-i2]
教训/收获
某一式子重复多次出现时,可以用引用创建别名,方便编码
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=100500;
const int inf=0x3f3f3f3f;
int n,m;
template <typename T>
void read(T &x){
x=0;
char ch=getchar();
ll f=1;
while(!isdigit(ch)){
if(ch=='-')
f*=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
x*=f;
}
int dp[25][15][15],a[15][15];
int main(){
IOS
//freopen("D:\\VScode\\IO\\in.txt","r",stdin);
//freopen("D:\\VScode\\IO\\out.txt","w",stdout);
int n;
cin>>n;
int x,y,z;
while(cin>>x>>y>>z){
if(x==0&&y==0&&z==0)
break;
a[x][y]=z;
}
for(int k=2;k<=2*n;k++){
for(int i1=1;i1<=n;i1++){
for(int i2=1;i2<=n;i2++){
int j1=k-i1,j2=k-i2;
if(j1<1||j2<1||j1>n||j2>n)
continue;
int w=a[i1][j1];
if(j1!=j2)
w+=a[i2][j2];
int &x=dp[k][i1][i2];
x=max(x,dp[k-1][i1-1][i2]+w);
x=max(x,dp[k-1][i1][i2-1]+w);
x=max(x,dp[k-1][i1][i2]+w);
x=max(x,dp[k-1][i1-1][i2-1]+w);
}
}
}
cout<<dp[2*n][n][n];
}