最近经常能在比赛的时候遇到dp有关的题目,而且蓝桥杯也经常会考dp,由此先刷一道基础题练练手,并在此写一篇题解,希望能帮到和我一样在dp方面很苦手的人。
题目链接:P9325 [CCC 2023 S2] Symmetric Mountains - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目大意(摘自洛谷):
Rebecca 有一张落基山脉的照片,上面排列着 N(1≤N≤5000) 座山,从左向右的第 i 座山的高度是 hi(1≤hi≤105)。Rebecca 截图保留照片的一个连续段,这张截图的不对称性定义为:处于截图上对称位置的山的高度差的绝对值之和(截图最左和最右的山的高度差,左数第二和右数第二的山的高度差,诸如此类的和)。
Rebecca 想要知道对于长度为 i 的截图,拥有的最小不对称性。请你对于 1≤i≤N,输出这个值。
解析:这道题如果每次枚举区间长度与起点的同时再暴力枚举一遍区间内对称的元素时间复杂度会达到,在数据5000的情况下必定超时,这时我们就需要借助区间dp,表示[i,j]区间范围内的最小高度差,由于在计算的过程中每次作差我们都是在对称的位置,所以在转移时是由上一个对称的区间转移过来,即[i+1,j-1],由此得到状态转移方程:
在每次枚举的时候枚举最大值即可,时间复杂度。
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
using namespace std;
using ll=long long ;
using pii=pair<int,int>;
using pll=pair<ll,ll>;
#define endl "\n"
constexpr int N=5010;
constexpr int mod=1e9+7;
int n;
int a[N];
int f[N][N];
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
f[i][i]=0;
cout<<0<<" ";
for(int len=2;len<=n;len++)
{
int res=0x3f3f3f3f;
for(int i=1;i+len-1<=n;i++)
{
int j=i+len-1;
f[i][j]=f[i+1][j-1]+abs(a[j]-a[i]);
res=min(res,f[i][j]);
}
cout<<res<<" ";
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
t=1;
while(t--)
solve();
return 0;
}
总结:区间dp的关键在于明确区间是怎么转移过来的,才能正确找出状态转移方程。