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绝了,上升子序列一定要记录最后一位!!!

posted @ 2021-02-07 12:11  acmloser  阅读(63)  评论(0编辑  收藏  举报