AcWing 273. 分级
考察:线性DP
思路:
通过证明(dfs打表) 可以发现存在一个最优解,它的数字全部在A数组里出现过.
考虑根据最后一个数字设置状态转移方程,f[i][j]代表考虑前i个数,最后一个数字是a[j](b数组是非严格单调序列,和上题一样考虑,我们需要知道它的末尾值).f的属性就是和的最小值.去掉最后一个确定的数字,根据LIS问题我们考虑倒数第二个数字,f[i][j] = f[i-1][k](k<=j)+abs(a[i]-b[j])//b是非严格单调序列.根据状态转移方程我们知道,需要三重循环枚举i,j,k.但是根据上题可以用一个变量代替第三重循环,也就是求出了前k-1个数的最小值,前k个数的最小值就是再与b[k]取min即可.
我们对a数组排序,得到b数组,按顺序枚举b,这样就能保证b数组是单调的
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 typedef long long ll; 7 const int N = 2010; 8 int a[N],b[N],n; 9 ll f[N][N]; 10 ll dp() 11 { 12 ll ans = 0x3f3f3f3f; 13 for(int i=1;i<=n;i++) 14 { 15 ll minv = 0x3f3f3f3f; 16 for(int j=1;j<=n;j++) 17 { 18 minv = min(f[i-1][j],minv); 19 f[i][j] = minv+abs(a[i]-b[j]); 20 } 21 } 22 for(int i=1;i<=n;i++) ans = min(f[n][i],ans); 23 return ans; 24 } 25 int main() 26 { 27 scanf("%d",&n); 28 for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i] = a[i]; 29 sort(b+1,b+n+1); 30 ll ans = dp(); 31 reverse(a+1,a+n+1); 32 ans = min(dp(),ans); 33 printf("%lld\n",ans); 34 return 0; 35 }
2021.3.12 二刷没做出来,但是选b[j]作为结尾实在巧妙,选b[j]作结尾,使得能快速找到构造i-1个数,且结尾<=b[j]的f最小值.第i个数确定选b[j],所以不会出现第i-1个数>第i个数的现象.
这才隔了几天又忘了2333绝了,上升子序列一定要记录最后一位!!!