HDU3507 Print Article 斜率优化DP

P r i n t   A r t i c l e Print\ Article Print Article

Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 22693 Accepted Submission(s): 6866

Problem Description:
Zero has an old printer that doesn’t work well sometimes. As it is antique, he still like to use it to print articles. But it is too old to work for a long time and it will certainly wear and tear, so Zero use a cost to evaluate this degree.
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost

( ∑ i = 1 k C i ) 2 + M (\sum_{i=1}^{k}C_i)^2+M (i=1kCi)2+M

M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.

Input:
There are many test cases. For each test case, There are two numbers N and M in the first line (0 ≤ n ≤ 500000, 0 ≤ M ≤ 1000). Then, there are N numbers in the next 2 to N + 1 lines. Input are terminated by EOF.

Output:
A single number, meaning the mininum cost to print the article.

Sample Input:
5 5
5
9
5
7
5

Sample Output:
230


题解:

首先我们求出C数组的前缀和数组s
显然能想到一个状态转移方程:

f [ i ] = m i n j = 0 i − 1 ( f [ j ] + ( s [ i ] − s [ j ] ) 2 + M ) f[i]=min_{j=0}^{i-1}(f[j]+(s[i]-s[j])^2+M) f[i]=minj=0i1(f[j]+(s[i]s[j])2+M)

复杂度为 O(n^2) 当然直接T爆,所以要使用斜率优化来降低复杂度
现在我们假设j>k并且从 j 向 i 转移比从 k 向 i 转移更优,那么可以有下面的不等式:
f [ j ] + ( s [ i ] − s [ j ] ) 2 + M < = f [ k ] + ( s [ i ] − s [ k ] ) 2 + M   ① f[j]+(s[i]-s[j])^2+M<=f[k]+(s[i]-s[k])^2+M\ ① f[j]+(s[i]s[j])2+M<=f[k]+(s[i]s[k])2+M 
化简:
f [ j ] + s [ i ] 2 − 2 s [ i ] s [ j ] + s [ j ] 2 < = f [ k ] + s [ i ] 2 − 2 s [ i ] s [ j ] + s [ k ] 2 f[j]+s[i]^2-2s[i]s[j]+s[j]^2<=f[k]+s[i]^2-2s[i]s[j]+s[k]^2 f[j]+s[i]22s[i]s[j]+s[j]2<=f[k]+s[i]22s[i]s[j]+s[k]2
f [ j ] − f [ k ] < = 2 s [ i ] s [ j ] − 2 s [ i ] s [ j ] − s [ j ] 2 + s [ k ] 2 f[j]-f[k]<=2s[i]s[j]-2s[i]s[j]-s[j]^2+s[k]^2 f[j]f[k]<=2s[i]s[j]2s[i]s[j]s[j]2+s[k]2
( f [ j ] + s [ j ] 2 ) − ( f [ k ] − s [ k ] 2 ) 2 ( s [ j ] − s [ k ] ) < = s [ i ] \frac{(f[j]+s[j]^2)-(f[k]-s[k]^2)}{2(s[j]-s[k])}<=s[i] 2(s[j]s[k])(f[j]+s[j]2)(f[k]s[k]2)<=s[i]
左端项很像是求斜率的公式,也就是说当 j k 满足以上不等式的时候 j点 绝对优于 k点,由于s[i]是单调递增的,所以对于之后所有的点,这个不等式都是成立的,也就是说这个 k点是无用点了,不用保留了
接下来我们令:
g [ i , j ] = ( f [ i ] + s [ i ] 2 ) − ( f [ j ] − s [ j ] 2 ) 2 ( s [ i ] − s [ j ] ) , i > j g[i,j]=\frac{(f[i]+s[i]^2)-(f[j]-s[j]^2)}{2(s[i]-s[j])}, i>j g[i,j]=2(s[i]s[j])(f[i]+s[i]2)(f[j]s[j]2),i>j
假设现在有三个点 i,j,k且k,有 g[i,j]<=g[j,k]
那么 j点就可以不保留,为什么呢
分三种情况来讨论

1. s [ i ] < = g [ i , j ] < = g [ j , k ] s[i]<=g[i,j]<=g[j,k] s[i]<=g[i,j]<=g[j,k]

  • j点优于i
  • k点优于j

2. g [ i , j ] < = s [ i ] < = g [ j , k ] g[i,j]<=s[i]<=g[j,k] g[i,j]<=s[i]<=g[j,k]

  • i点优于j
  • k点优于j

3. g [ i , j ] < = g [ j , k ] < = s [ i ] g[i,j]<=g[j,k]<=s[i] g[i,j]<=g[j,k]<=s[i]

  • i点优于j
  • j点优于k

根据这三种情况发现不管怎样j点都不是最优点,所以当g[i,j]<=g[j,k]② 时可以直接删去 j点
我们可以用一个单调队列来维护斜率
当条件①满足时删除队首元素
当条件②满足时删除队尾元素
每次只要取队首的元素进行转移即可

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
using LL = int_fast64_t;
const int MAXN = 5e5+7;
int n;
LL A[MAXN],m,f[MAXN],s[MAXN];
LL calu(int i, int j){ return f[i]+s[i]*s[i]-f[j]-s[j]*s[j]; }
LL cald(int i, int j){ return 2*(s[i] - s[j]); }
int main(){
    ____();
    while(cin >> n >> m){
        for(int i = 1; i <= n; i++) cin >> A[i];
        for(int i = 1; i <= n; i++) s[i] = s[i-1] + A[i];
        deque<int> que;
        que.push_back(0);
        for(int i = 1; i <= n; i++){
            while(que.size()>=2){
                int k = que.front();
                que.pop_front();
                int j = que.front();
                que.push_front(k);
                if(calu(j,k)<=cald(j,k)*s[i]) que.pop_front();
                else break;
            }
            f[i] = f[que.front()] + (s[i] - s[que.front()]) * (s[i] - s[que.front()]) + m;
            while(que.size()>=2){
                int j = que.back();
                que.pop_back();
                int k = que.back();
                que.push_back(j);
                if(calu(i,j)*cald(j,k)<=calu(j,k)*cald(i,j)) que.pop_back();
                else break;
            }
            que.push_back(i);
        }
        cout << f[n] << endl;
    }
    return 0;
}

你可能感兴趣的:(DP优化)