P2893 [USACO08FEB]修路
直入主题。 农夫约翰想改造一条路,原来的路的每一段海拔是Ai,修理后是Bi花费|A_i–B_i|。我们要求修好的路是单调不升或者单调不降的。求最小花费。
数据范围:n<=2000,0≤ Ai ≤ 1,000,000,000
(说真的,时隔几个月,发现这题其实挺简单的)
最一开始,就打了一个贪心。
#include<bits/stdc++.h>//本人打题目时很喜欢万能头 using namespace std; const int maxn=2010; long long n,ans1,ans2; long long a[maxn]; int main() { scanf("%lld",&n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } for(int i=1;i<n;i++) { if(a[i]>a[i+1]) ans1+=abs(a[i]-a[i+1]); //printf("%d\n",ans1); } for(int i=n;i>1;i--) { if(a[i]>a[i-1]) ans2+=abs(a[i-1]-a[i]); //printf("%d\n",ans2); } printf("%d",min(ans1,ans2)); return 0; }
此贪心思路 很容易发现,如果想要最小值的话,凹下去的土块一定会被填起来,而且填的高度一定会使这个土块和两侧最低的那个相平(比如样例第三个土块,填起来一定会和 二 一样高)于是考虑跑两遍,一个单增,一个单减。
结果:40分
。。。
随手一组hack数据:
9 5 5 5 1 1 1 5 5 5;
我没有及时更新高度,以至于会有后效性。 于是我每次减完会把它更新一次高度。 但是发现,还是会有后效性,hack数据还是毒瘤。
我终于想到了dp(我恨dp)
接下来考虑转移方程式 考虑第i个土块,对其有影响的数据只有前i个土块的高度,还有本土块的高度,所以: 我们用dp[i][j]将前i段变作不下降序列,且第j段道路的高度为j时的最小花费
但是,j的枚举会把时间炸上天!!!!
通过前面的结论,我们发现,最小的填补一定会在目标高度中出现,所以我们开另外一个数组,把高度存到里面,排序,从里面找一个合适的Ai,就省去了遍历1—100000000的时间
继续方程式。写出来就是这样,前i个柱子最小(此为单调不递减)
此题,洛谷数据过于划水以至于只考虑单增就可以过了。
于是放代码(只放单增的了)
#include<bits/stdc++.h>
using namespace std;
const int inf=0x7fffffff;
const int maxn=2010;
int n;
int a[maxn],b[maxn];
int dp[maxn][maxn];
int main()
{
int ans=inf;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(j==1)//因为循环边界,所以当j==1我们要特判一发,把1拍到最低
dp[i][j]=dp[i-1][j]+abs(a[i]-b[j]);
else
dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(a[i] - b[j]));//拍土了
if(i==n)
ans=min(dp[i][j],ans);
}
}
printf("%d\n",ans);
return 0;
}
(膜拜一下隔壁dp神仙)