第一行为一个整数N表示战线的总长度。
第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai。
标签:
第一行为一个整数N表示战线的总长度。
第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai。
共一个整数,表示最小的战线花费值。
1<=N<=10^6,1<=Ai<=10^9
这道题显然是一道dp的题。一眼看去,显然可以令f[i]表示第i个位置放防御塔的费用(先说明一下,为了方便计算,我把序列反向后从左到右计算),那么显然有:f[i]=min(f[j]+sigma(1~i-j-1))+a[i]。然而这是个n^2的dp方程,那么怎么做呢?翻了翻网上的题解,发现几乎全部都是写得斜率优化。然而xzy神犇告诉我决策单调性可做。
怎么做呢?不难发现对于位置i有两个决策j,k(j < k)满足k比j优,那么对于位置大于i的位置也满足这个。证明如下:
若对于j<k且f[j]+sigma(1~i-j-1)>f[k]+sigma(1~i-k-1)
因为j<k 所以对于x>i有sigma(1~x-j-1)-sigma(1~i-j-1)>sigma(1~x-k-1)-sigma(1~i-k-1)
所以f[j]+sigma(1~x-j-1)>f[k]+sigma(1~x-k-1)
于是我们就可以做了。我们维护一个单调队列,每次先把队首用不到的区间先弹掉,再用队首元素来更新当前答案,最后用这个解来更新后面的解。当我们发现 对于队尾区间的左端点当前解比队列中储存的解更优时,我们可以直接把这个区间给弹掉(想一想,为什么)。这样弹完以后,若队列中已经没有区间,我们就可以将当前解的区间直接加入队列;否则当前解最优的边界就在队尾的区间中,我们需要在这个区间内二分把这一个边界给找出来,然后更改这个区间的右边界并插入区间。
这道题是我练习决策单调性优化dp的第一题,总的来说还是有一点难度的。代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 7 #define maxn 1000010 8 #define INF (1LL<<50) 9 10 using namespace std; 11 typedef long long llg; 12 13 int n,a[maxn],l[maxn],r[maxn],x[maxn],lz,rz; 14 llg f[maxn]; 15 16 int getint(){ 17 int w=0;bool q=0; 18 char c=getchar(); 19 while((c>‘9‘||c<‘0‘)&&c!=‘-‘) c=getchar(); 20 if(c==‘-‘) q=1,c=getchar(); 21 while(c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); 22 return q?-w:w; 23 } 24 25 inline bool pd(int i,int j,int x){ 26 return (f[i]+((llg)(x-i)*(x-i-1)>>1))<=(f[j]+((llg)(x-j)*(x-j-1)>>1)); 27 } 28 29 int main(){ 30 File("a"); 31 n=getint(); 32 for(int i=n;i;i--) a[i]=getint(),f[i]=INF; 33 f[1]=a[1]; f[n+1]=INF; 34 l[rz]=2,r[rz]=n+1,x[rz++]=1; //第一个解需要先处理好 35 for(int i=2;i<=n+1;i++){ 36 while(r[lz]<i && lz<rz) lz++; //弹掉没用的区间 37 f[i]=f[x[lz]]+((llg)(i-x[lz])*(i-x[lz]-1)>>1); //更新当前解 38 while(rz>lz && pd(i,x[rz-1],l[rz-1])) rz--; //从队尾弹掉没有当前解优的区间 39 if(lz==rz) l[rz]=i+1,r[rz]=n+1,x[rz++]=i; //直接插入 40 else{ 41 int ll=l[rz-1],rr=r[rz-1]+1,mid,xx=x[rz-1]; //在队尾区间内二分 42 while(ll!=rr){ 43 mid=ll+rr>>1; 44 if(pd(i,xx,mid)) rr=mid; 45 else ll=mid+1; 46 } 47 r[rz-1]=ll-1; l[rz]=ll,r[rz]=n+1,x[rz++]=i; //修改并插入 48 } 49 } 50 printf("%lld",min(f[n],f[n+1])); 51 return 0; 52 }
标签:
原文地址:http://www.cnblogs.com/lcf-2000/p/5574163.html