做题笔记,CF 4.16Div2 C CF1529C The Sports Festival(贪心+DP)
题目大意
给你一个序列,你可以对其任意排列。
要求,从1到n,每个位置取之前所有位置上值的最大值-最小值,使总和最小
(
n
<
=
2000
,
a
[
i
]
<
=
1
e
9
)
(n<=2000,a[i]<=1e9)
(n<=2000,a[i]<=1e9)
做法
我当时的直觉:结论应该是,至少应该长得像“把中间的尽可能排前面,把最大最小的丢后面”,因为一但最大的最小的在前面出现了,那他们显然会一直引入在答案中计算,而直接放最后只会算一次。
所以,初步的思路是把最大的或者最小的扔到后面
那么这个思想放在1个区间的元素更新上,我们不出意外也是把最后一个放成最大的或者最小的,那么如果我们用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]来表示从i到j这个区间的最优值,它必然从两种状态更新得到
即:
把最大的放在最后或者把最小的放在最后
如果我们的
a
[
i
]
a[i]
a[i]是有序的(我们可以排个序),那自然比上步多的代价就是最大值-最小值
d
p
[
i
]
[
j
]
=
m
i
n
(
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
+
1
]
[
j
]
)
+
a
[
j
]
−
a
[
i
]
;
dp[i][j]=min(dp[i][j-1],dp[i+1][j])+a[j]-a[i];
dp[i][j]=min(dp[i][j−1],dp[i+1][j])+a[j]−a[i];
那就是区间DP咯
其实不难,那天晚上一定是太困了,脑子僵硬了没想到逆推
也可能就是菜,不太会dp
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[2005][2005];
int n;
int a[2005];
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
for(int k=1;k<=n;k++){
for(int l=1;l<=n;l++){
int r=l+k-1;
if(r>n) continue;
dp[l][r]=min(dp[l][r-1],dp[l+1][r])+a[r]-a[l];
}
}
printf("%lld",dp[1][n]);
}