动态规划之坐标DP

定义

坐标型动态规划一般是给定网格、序列,求满足条件的MAX或MIN。
开数组时,dp[i]一般代表以ai结尾的满足条件的子序列,dp[i][j]代表以i、j结尾的满足条件的最优解

例题

数塔

典中典

变形

晴天小猪历险记之Hill
抓苹果
免费馅饼

矩阵取数

描述

传送门

思路

首先看出,每行的问题是独立的、互不影响,可以对每一行分别求解最高分,相加得出整个游戏的最高分。
就每一行而言,取数的顺序是有讲究的,先取的数的倍数较少,后取的数的倍数较大,因此要找到最优的取数方案。
我们设计动规的状态时,常设置状态为选取了前i个数,但这题的规则是每次从开头或者末尾取,这是差异,那怎么调整呢?应该回到动规性质上来考虑,状态设置的要求是无后效性,即之前的选择不影响之后的选择,当前的选择是过去的完整总结。不妨设想我们玩这个游戏玩到某一步时,我们已从行首取了3个数,从行尾取了5个数,还有中间10个数可以取,那接下来的游戏都是在这10个数中进行,跟之前的前3个、后5个已经没有关系了。这么思考很容易设计出:dp[i][j]表示选取了前i个数和后j个数。这里要注意的是,题目规则中的2阶乘加权是由取数的轮次决定的,这个次数只与i和j相关,不受之前所取数的影响,因此这个状态满足无后效性原则。又因为分数是一直简单累加的,所以当前最优解必然促成全局最优解,满足最优子结构性质。
状态转移比较容易构造,dp[i][j]由dp[i-1][j]或dp[i][j-1]转移而来,分别意味着选取了a[i]和选取了a[m+1-j]。dp[0][0]=0
dp[i][j]=max(2i+j *a[i]+dp[i-1][j],2i+j *a[m+1-j]+dp[i][j-1])
注意迭代的顺序,要按选取数量从小到大的顺序进行迭代,i、j循环须按从小到大顺序。

しかし(但是),让我们回看一下数据范围1≤n,m≤80,0≤ai,j≤10000,你有没有想到什么?
没错,\(\color{red}{\LARGE 高精度!}\) 这也是这道题的难点之一(我用的是__int128,以后会发文讲解)
好了,不多说了,上代码

code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define Elaina 0
const int N=105;
long long n,m,a[N][N];
__int128 ans=0,dp[N][N][N];
void print(__int128 x){
	if(x>9)print(x/10);
	putchar(x%10+'0');
}
void read(__int128 &res){
	char scan[1005];
	res=0;
	cin>>scan;
	for(int i=0;i<strlen(scan);i++){
		res=res*10+scan[i]-'0';
	}
} 
void DP(){
	for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            dp[i][j][j]=a[i][j]*2;
    for(int i=1;i<=n;++i){
        for(int len=2;len<=m;++len){
            for(int l=1;l+len-1<=m;++l){
                int r=l+len-1;
                dp[i][l][r]=max(dp[i][l][r],max(dp[i][l][r-1]*2+a[i][r]*2,dp[i][l+1][r]*2+a[i][l]*2));
            }
        }
        ans += dp[i][1][m];
    }	
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        	cin>>a[i][j]; 
	DP();
	print(ans);
	return Elaina;
} 
posted @ 2024-02-17 20:27  Elaina_0  阅读(16)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end