10403: D.山区修路
Time Limit: 2 Sec Memory Limit: 128 MB Submit: 69 Solved: 23 [Submit][Status][Web Board]
Description
某山区的孩子们上学必须经过一条凹凸不平的土路,每当下雨天,孩子们非常艰难。现在村里走出来的Dr. Kong决定募捐资金重新修建着条路。由于资金有限,为了降低成本,对修好后的路面高度只能做到单调上升或单调下降。
为了便于修路,我们将整个土路分成了N段,每段路面的高度分别A1,A2,….,An。由于将每一段路垫高或挖低一个单位的花费成本相同,修路的总费用与路面的高低成正比。
现在Dr. Kong希望找到一个恰好含N个元素的不上升或不下降序列B1,B2,….,Bn,作为修过的路路段的高度。要求:
| A1-B1| + | A2–B2| + ... + | An-Bn|------>最小
Input
第一行: K 表示有多少组测试数据。
接下来对每组测试数据:
第1行: N 表示整个土路分成了N段
第2~N+1行: A1 A2 ……AN 表示每段路面的高度
2≤k≤10 0≤Ai≤107 0≤N≤500 (i=1,…, N)
所有数据都是整数。 数据之间有一个空格。
数据保证| A1-B1|+| A2-B2|+ ... +| An-Bn|的最小值不会超过109
Output
对于每组测试数据,输出占一行:| A1-B1|+| A2-B2|+ ... +| An-Bn|的最小值。
Sample Input
2 7 1 3 2 4 5 3 9 5 8 6 5 6 2
Sample Output
3 1
HINT
Source
题解:把一串序列变为一段连续不增,或者连续不减的最小花费;
dp思想;dp[i][j]代表第i个元素换为第j个值的最小花费;
可列出状态转移方程:dp[i][j]=abs(m[i]-n[j])+mn;mn为转化为1~j间的最小花费;
由于是单调递增或者单调递减,只需要升序降序下n数组就可以了,对了,n数组是离散化后的数组;单增或者单减;
代码:
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define mem(x,y) memset(x,y,sizeof(x)) #define SI(x) scanf("%d",&x) #define SL(x) scanf("%lld",&x) #define PI(x) printf("%d",x) #define PL(x) printf("%lld",x) #define P_ printf(" ") const int INF=0x3f3f3f3f; const double PI=acos(-1.0); typedef long long LL; const int MAXN=510; int m[MAXN],n[MAXN]; int dp[MAXN][MAXN]; int N; int cmp(int a,int b){ return a>b; } int solve(){ int mn; for(int j=1;j<=N;j++)dp[1][j]=abs(m[1]-n[j]); for(int i=2;i<=N;i++){ mn=INF; for(int j=1;j<=N;j++){ mn=min(mn,dp[i-1][j]); dp[i][j]=abs(m[i]-n[j])+mn; } } int ans=INF; for(int i=1;i<=N;i++){ ans=min(ans,dp[N][i]); } return ans; } int main(){ int T; SI(T); while(T--){ SI(N); for(int i=1;i<=N;i++)SI(m[i]),n[i]=m[i]; int ans1,ans2; sort(n+1,n+N+1); ans1=solve(); sort(n+1,n+N+1,cmp); ans2=solve(); printf("%d\n",min(ans1,ans2)); } return 0; }
有大神用左偏树,划分树写的。。。。
链接:http://blog.163.com/hacker_james/blog/static/659024432011711105241183/
人家的思路:
2.左偏树(leftist) O(nlogn)
左偏树作为一种可并堆在这里可以起到作用
我们将每来一个点,把它单独建一棵左偏树,然后跟前一区间的左偏树中的所存的中位数比较,若小于,则和前一区间的左偏树合并,知道比前一区间所记录的中位数是小于或等于当前区间的中位数。
怎样用左偏树记录中位数? 很简单,每棵左偏树只保存(n+1)/2 个元素,则树顶所保存的最大值即为中位数
注意的是,用左偏树来求中位数在某些情况下是错误的。按照HYH大神的做法,假设现在对于两个序列 4 5 6 7 8 9 和 1 2 3,合并后其中位数是5。然而,按照左偏树只存(n+1)/2 个元素的方法,前者只保留 4 5 6, 后者保留 1 2,两者合并后保留5个元素,所得到的中位数却是6..
但这道题却没影响,至于为什么? 没想明白.....
3.划分树
O(nlogn)
既然要求中位数,而且数列又是静态数列,可以想到用划分树来求.划分树除了空间比左偏树大一点之外,执行效率和正确率都比左偏树要好。左偏树的常数相对比较大。