题目评测在这里
http://www.lydsy.com/JudgeOnline/problem.php?id=1010
一道很明显的1D1D动态规划,方程很好写
f[i]=min{ f[j] + ( i-(j+1)+s[i]-s[j]-l )2}
直接写的话500000的数据量会让你明白。
两种做法:
①四边形不等式优化
很明显的,用w[i+1,j]表示上述方程里的( i-(j+1)+s[i]-s[j]-l )2,不难验证这是个满足单调性的,也就是说对于这个方程,决策是单调的
那么根据决策单调性,很自然的就有O(nlogn)的算法了
1 /** 2 *Prob : HNOI 2008 toy 3 *Sol : 1D1D dp -- 决策单调性 4 *Data : 2012-6 5 *Author : Zhou Hang 6 */ 7 8 #include <cmath> 9 #include <algorithm> 10 #include <cstdio> 11 #include <cstring> 12 #include <iostream> 13 14 #define MaxN 55000 15 #define lld long long 16 17 18 using namespace std; 19 20 struct node 21 { 22 int x,y;//决策适用区间 23 int pos; //由pos转移得到 24 } q[MaxN]; 25 26 int n,l; 27 lld f[MaxN],c[MaxN]; 28 29 lld sqr(lld x) 30 { 31 return x*x; 32 } 33 34 lld cal(int x,int y) 35 { 36 return sqr(x-(y+1)+(c[x]-c[y])-l)+f[y]; 37 } 38 39 void dp() 40 { 41 memset(f,127,sizeof(f)); 42 f[0]=0; 43 int head=1,tail=1; 44 q[1].x=1; q[1].y=n; q[1].pos=0; 45 46 int l,r,mid; 47 48 for (int i=1; i<=n; i++) 49 { 50 //算出f[i] 51 f[i]=cal(i,q[head].pos); 52 if (i+1>q[head].y) head++; 53 54 //差的决策整体淘汰 55 while (head<=tail && cal(q[tail].x,i)<cal(q[tail].x,q[tail].pos) ) 56 tail--; 57 58 //对列为空 59 if (head>tail) 60 { 61 q[++tail].x=i+1; 62 q[tail].y=n; q[tail].pos=i; 63 continue; 64 } 65 66 //二分,留下当前决策的一部分 67 l=q[tail].x; r=q[tail].y+1; 68 while (l+1<r) 69 { 70 mid=(l+r)/2; 71 if ( cal(mid,i)<cal(mid,q[tail].pos) ) r=mid; 72 else l=mid; 73 } 74 if (r>=n+1) continue; 75 76 q[tail].y=l; 77 q[++tail].x=r; 78 q[tail].y=n; 79 q[tail].pos=i; 80 } 81 82 } 83 84 85 int main() 86 { 87 freopen("toy.in","r",stdin); 88 freopen("toy.out","w",stdout); 89 90 scanf("%d%d",&n,&l); 91 c[0]=0; 92 for (int i=1; i<=n; i++) 93 { 94 scanf("%d",&c[i]); 95 c[i]+=c[i-1]; 96 } 97 98 dp(); 99 100 cout<<f[n]<<endl; 101 102 fclose(stdin); fclose(stdout); 103 return 0; 104 }
②斜率优化 O(n)
个人觉得实际上斜率优化就是凸完全单调性的一种特殊情况,关于斜率优化的使用方法,这个人条理很清晰,觉得很好,受到了很大启发
在这里谢一声http://blog.sina.com.cn/s/blog_5e6fc6d60100vp2j.html
关于斜率优化的讲解,http://wenku.baidu.com/view/d3d979dcd15abe23482f4d58.html这个里面讲的很好
这道题直接说化简方程后的结果了:((f[x]+s'[x]*s'[x])-(f[y]+s'[y]*s'[y]))/(s'[x]-s'[y])>2*(s'[i]-L') 其中s'[i]=s[i]+i,L'=L+1
G(x,y)=((f[x]+s'[x]*s'[x])-(f[y]+s'[y]*s'[y]))/(s'[x]-s'[y])
具体看代码
1 /** 2 *Prob : HNOI 2008 toy 3 *Sol : 1D1D dp -- 斜率优化 4 *Data : 2012-6 5 *Author : Zhou Hang 6 */ 7 8 #include <cmath> 9 #include <algorithm> 10 #include <cstdio> 11 #include <cstring> 12 #include <iostream> 13 14 #define MaxN 55000 15 #define lld long long 16 17 using namespace std; 18 19 int q[MaxN]; 20 int n,l; 21 lld f[MaxN],s[MaxN]; 22 23 double G(int x,int y) 24 { 25 return ((f[x]+s[x]*s[x])-(f[y]+s[y]*s[y]))/(s[x]-s[y]); 26 } 27 28 29 void dp() 30 { 31 memset(f,127,sizeof(f)); 32 f[0]=0; q[1]=0; 33 int head=1,tail=1; 34 35 for (int i=1;i<=n;++i) 36 { 37 while (head<tail && G(q[head],q[head+1])<=2*(s[i]-l-1)) 38 head++; 39 40 f[i]=f[q[head]]+(s[i]-s[q[head]]-1-l)*(s[i]-s[q[head]]-1-l); 41 42 while (head<tail && G(q[tail-1],q[tail])>=G(q[tail],i)) 43 tail--; 44 q[++tail]=i; 45 } 46 cout<<f[n]<<"\n"; 47 } 48 49 int main() 50 { 51 freopen("toy.in","r",stdin); 52 freopen("toy.out","w",stdout); 53 54 scanf("%d%d",&n,&l); 55 s[0]=0; 56 for (int i=1; i<=n; i++) 57 { 58 scanf("%d",&s[i]); 59 s[i]+=s[i-1]+1; 60 } 61 //s'[i]=s[i]+n 用s[i]存s'[i] 62 63 dp(); 64 65 66 fclose(stdin); fclose(stdout); 67 return 0; 68 }