题意:
有一长度为n的正整数序列,你可以选择K个数字任意改变它,使得$max { a(i+1) - a(i) } $ 最小,求最小值。
解法:
1.$O(n^2log(MAX_A) )$,考虑二分出$d = max { a(i+1) - a(i) } $,这样考虑dp
$f(i,j)$表示前i个数字,末位的数字为j的时候最少修改了多少数字
$f(i,j) = min { f(i-1,k) } (j-d ≤ k ≤ j+d,j = a(i))$
$f(i,j) = min { f(i-1,k) } (j-d ≤ k ≤ j+d,j ≠ a(i))$
注意到有效的j只有$a(i),a(i)-d,a(i)+d$,从而对第二维离散化,同时用单调队列维护区段min即可
(维护方法,对于 前面出现的 比后面的数字小 的数一定不会出现在答案中,这样只要维护一个单调增的双端队列即可)
此方法常数过大,TLE
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <ctime> 6 7 #define LL long long 8 #define N 2010 9 #define INF 0x3f3f3f3f 10 11 using namespace std; 12 13 int n,m,K,minh,maxh,tots; 14 int a[N],a0[N*3]; 15 int f[N][N*3],q[N*3]; 16 double tott; 17 18 int Abs(int x) 19 { 20 if(x<0) return -x; 21 return x; 22 } 23 24 bool check(int d) 25 { 26 a0[0]=0; 27 for(int i=1;i<=n;i++) 28 { 29 a0[++a0[0]]=a[i]; 30 if(a[i]-(LL)d>=(LL)minh) a0[++a0[0]]=a[i]-d; 31 if(a[i]+(LL)d<=(LL)maxh) a0[++a0[0]]=a[i]+d; 32 } 33 sort(a0+1,a0+a0[0]+1); 34 m=1; 35 for(int i=2;i<=a0[0];i++) if(a0[i]!=a0[i-1]) a0[++m]=a0[i]; 36 for(int i=1;i<=m;i++) f[1][i]=1; 37 int t1=lower_bound(a0+1,a0+m+1,a[1])-a0; 38 f[1][t1]=0; 39 int st=1,ed=0; 40 for(int i=2;i<=n;i++) 41 { 42 st=1, ed=0; 43 int tmp=1,minv=K+1; 44 for(int j=1;j<=m;j++) 45 { 46 while(tmp<=m && a0[tmp]<=a0[j]+d) 47 { 48 while(st<=ed && f[i-1][q[ed]]>=f[i-1][tmp]) ed--; 49 q[++ed]=tmp++; 50 } 51 while(st<ed && a0[q[st]]<a0[j]-d) st++; 52 int k=q[st]; 53 if(a0[j]==a[i]) f[i][j]=f[i-1][k]; 54 else f[i][j]=f[i-1][k]+1; 55 minv = min(minv, f[i][j]); 56 if(f[i][j] + n-i <= K) return 1; 57 } 58 if(minv > K) return 0; 59 } 60 return 1; 61 } 62 63 int main() 64 { 65 freopen("test.txt","r",stdin); 66 while(~scanf("%d%d",&n,&K)) 67 { 68 unsigned int l=0,r=0; 69 a0[0]=0; 70 minh=INF; 71 maxh=-INF; 72 for(int i=1;i<=n;i++) 73 { 74 scanf("%d",&a[i]); 75 minh=min(minh,a[i]); 76 maxh=max(maxh,a[i]); 77 if(i>1) r = max(r,(unsigned int)Abs(a[i]-a[i-1])); 78 } 79 while(r-l>2) 80 { 81 unsigned int mid=(l+r)>>1; 82 if(check(mid)) r=mid; 83 else l=mid; 84 } 85 for(unsigned i=l;i<=r;i++) 86 if(check(i)) 87 { 88 // cout << clock()/1000.0 << endl; 89 cout << i << endl; 90 break; 91 } 92 } 93 return 0; 94 }
2.注意到方法一中第二维实际只有 j=a(i) 和其他的j ,两种j。
从而设$f(i)$表示前i个数字,数字i不改变 最少修改了多少数字。
这样有
$f(i) = min { f(j) + j-i-1 }, ( frac{|a(i)-a(j)|}{i-j} ≤ d )$
(i到j之间的数字全都改变)
小常数飘过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <ctime> 6 7 #define LL long long 8 #define N 2010 9 #define INF 0x3f3f3f3f 10 11 using namespace std; 12 13 int n,m,K,a[N]; 14 int f[N]; 15 16 LL Abs(LL x) 17 { 18 if(x<0) return -x; 19 return x; 20 } 21 22 bool check(int d) 23 { 24 f[1]=0; 25 if(n-1<=K) return 1; 26 for(int i=2;i<=n;i++) 27 { 28 f[i]=i-1; 29 for(int j=1;j<i;j++) 30 if(Abs(a[i]-(LL)a[j]) <= d*(LL)(i-j)) 31 f[i] = min(f[i], f[j]+i-j-1); 32 if(f[i]+n-i<=K) return 1; 33 } 34 return 0; 35 } 36 37 int main() 38 { 39 // freopen("test.txt","r",stdin); 40 while(~scanf("%d%d",&n,&K)) 41 { 42 LL l=0,r=0; 43 for(int i=1;i<=n;i++) 44 { 45 scanf("%d",&a[i]); 46 if(i>1) r = max(r,(LL)Abs(a[i]-a[i-1])); 47 } 48 while(r-l>2) 49 { 50 LL mid=(l+r)>>1; 51 if(check(mid)) r=mid; 52 else l=mid; 53 } 54 for(unsigned i=l;i<=r;i++) 55 if(check(i)) 56 { 57 cout << i << endl; 58 break; 59 } 60 } 61 return 0; 62 }