Levko and Array

题意:

有一长度为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

 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 }
View Code

 

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之间的数字全都改变)

小常数飘过

 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 }
View Code

 

posted @ 2017-03-03 21:45  lawyer'  阅读(127)  评论(0编辑  收藏  举报