题目描述
输入
输出
样例输入
7
1
3
2
4
5
3
9
样例输出
3
提示
FJ将第一个高度为3的路段的高度减少为2,将第二个高度为3的路段的高度增加到5,总花费为|2-3|+|5-3| = 3,并且各路段的高度为一个不下降序列 1,2,2,4,5,5,9。
solution:
这个题考试的时候想的很暴力,f[i][j][1]表示到第i个点修改后高度为j且为非下降数列的最优情况,f[i][j][0]表示第i个点修改后高度为j且为非上升数列的最优情况,第一维可以用滚动数组,第二维为了扩大范围,可以将所有的高度减去其中个最小的高度,这些高度用第二维表示,很智障的想法骗了50分。
正解应该是离散加dp,因为高度取值太大,将每个高度离散并去重,按照高度的从小到大将这个数组排序,这样保证高度越大下标越大,我们第二维就可以用下标表示,不用开太大,节省空间。
f[i][j]=f[i-1][k]+abs(a[i]-j); j<=k;
g[i][j]=g[i-1][k]+abs(a[i]-j); j>=k;
如果这样三层for循环枚举应该会T掉两个点,这是我们可以用类似单调队列的思想,在枚举j的过程中因为高度数组从小到大已经排好序,所以其实我们已经知道了最小的f[i][k]和g[i][k],这样我们完全可以去掉第三层循环,然后就A啦。
考试时候的暴力dp:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 int read() { 8 int s=0,f=1; 9 char ch=getchar(); 10 while(ch>'9'||ch<'0') { 11 if(ch=='-') { 12 f=-1; 13 } 14 ch=getchar(); 15 } 16 while(ch>='0'&&ch<='9') { 17 s=(s<<1)+(s<<3)+(ch^48); 18 ch=getchar(); 19 } 20 return s*f; 21 } 22 int n,a[2005],minn=0x7fffffff,maxx; 23 int f[2][8000000][2];// 0 shenggao 1 jiangdi 24 int main() { 25 freopen("grading.in","r",stdin); 26 freopen("grading.out","w",stdout); 27 n=read(); 28 for(int i=1; i<=n; i++) { 29 a[i]=read(); 30 minn=min(minn,a[i]); 31 maxx=max(maxx,a[i]); 32 } 33 memset(f,0x6f,sizeof(f)); 34 int state=0; 35 for(int i=1; i<=n; i++) { 36 a[i]-=minn; 37 } 38 maxx-=minn; 39 for(int i=0; i<=maxx; i++) { 40 f[state][i][0]=f[state][i][1]=abs(a[1]-i); 41 } 42 for(int i=1; i<n; i++) { 43 int now=state^1; 44 for(int j=0; j<=maxx; j++) {// 0 shangsheng 1 xiajiang 45 if(f[state][j][0]>=1869573999&&f[state][j][0]>=1869573999) { 46 continue; 47 } 48 for(int k=0; k<=j; k++) { 49 f[now][k][1]=min(f[now][k][1],f[state][j][1]+abs(a[i+1]-k)); 50 } 51 for(int k=j; k<=maxx; k++) { 52 f[now][k][0]=min(f[now][k][0],f[state][j][0]+abs(a[i+1]-k)); 53 } 54 f[state][j][0]=f[state][j][1]=0x7fffffff; 55 } 56 state=now; 57 } 58 int ans=0x7ffffff; 59 for(int i=0; i<=maxx; i++) { 60 ans=min(ans,min(f[state][i][0],f[state][i][1])); 61 } 62 printf("%d",ans); 63 fclose(stdin); 64 fclose(stdout); 65 return 0; 66 }
正解:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 #define inf 0x7fffffff 7 int read() { 8 int s=0,f=1; 9 char ch=getchar(); 10 while(ch>'9'||ch<'0') { 11 if(ch=='-') { 12 f=-1; 13 } 14 ch=getchar(); 15 } 16 while(ch>='0'&&ch<='9') { 17 s=(s<<1)+(s<<3)+(ch^48); 18 ch=getchar(); 19 } 20 return s*f; 21 } 22 int n,size; 23 int a[2005],Hash[2005]; 24 int f[2005][2005],g[2005][2005]; 25 void Hash_init() { 26 sort(Hash+1,Hash+n+1); 27 size=unique(Hash+1,Hash+n+1)-Hash-1; 28 //cout<<size<<endl; 29 } 30 int Get_Hash(int x) { 31 return (lower_bound(Hash+1,Hash+size+1,x)-Hash); 32 } 33 void init() { 34 memset(f,0x3f,sizeof(f)); 35 memset(g,0x3f,sizeof(g)); 36 for(int i=1; i<=size; i++) { 37 f[1][i]=g[1][i]=abs(a[1]-Hash[i]); 38 } 39 } 40 int main() { 41 //freopen("grading.4.in","r",stdin); 42 n=read(); 43 for(int i=1; i<=n; i++) { 44 a[i]=Hash[i]=read(); 45 } 46 Hash_init(); 47 init(); 48 for(int i=2; i<=n; i++) { 49 int mx=inf; 50 for(int j=1; j<=size; j++) { 51 if(f[i-1][j]<mx) { 52 mx=f[i-1][j]; 53 } 54 f[i][j]=min(f[i][j],mx+abs(a[i]-Hash[j])); 55 } 56 mx=inf; 57 for(int j=size; j; j--) { 58 if(f[i-1][j]<mx){ 59 mx=g[i-1][j]; 60 } 61 g[i][j]=min(g[i][j],mx+abs(a[i]-Hash[j])); 62 } 63 } 64 int ans=0x7fffffff; 65 for(int i=1;i<=size;i++){ 66 ans=min(ans,min(f[n][i],g[n][i])); 67 } 68 printf("%d",ans); 69 return 0; 70 }