Division(斜率优化 四边形优化 dp)

原题: http://acm.hdu.edu.cn/showproblem.php?pid=3480

题意:

n个数排序后,分成多段,每一段是最大值减最小值的平方,求和的最小值。

解析:

老套路:

d p [ i ] [ j ] dp[i][j] 表示前面j个数分成i段的最小值,有 d p [ i ] [ j ] = m i n ( d p [ i 1 ] [ k ] + ( s [ j ] s [ k + 1 ] ) 2 ) dp[i][j]=min(dp[i-1][k]+(s[j]-s[k+1])^2) x < y x<y d p [ i 1 ] [ y ] dp[i-1][y] 优于 d p [ i 1 ] [ x ] dp[i-1][x] ,有 d p [ i 1 ] [ x ] + ( s [ j ] s [ x + 1 ] ) 2 > d p [ i 1 ] [ y ] + ( s [ j ] s [ y + 1 ] ) 2 dp[i-1][x]+(s[j]-s[x+1])^2>dp[i-1][y]+(s[j]-s[y+1])^2 ( d p [ i 1 ] [ x ] d p [ i 1 ] [ y ] + s [ x + 1 ] 2 s [ y + 1 ] 2 ) < 2 s [ j ] s [ y + 1 ] 2 s [ j ] s [ x + 1 ] -(dp[i-1][x]-dp[i-1][y]+s[x+1]^2-s[y+1]^2)<2s[j]s[y+1]-2s[j]s[x+1] ( d p [ i 1 ] [ x ] d p [ i 1 ] [ y ] + s [ x + 1 ] 2 s [ y + 1 ] 2 ) s [ y + 1 ] s [ x + 1 ] < 2 s [ j ] \dfrac{-(dp[i-1][x]-dp[i-1][y]+s[x+1]^2-s[y+1]^2)}{s[y+1]-s[x+1]}<2s[j]

再结合这道题进行分析:

  1. 问: 首先是二维的问题,第一次写会比较懵逼。在做dp[i]的时候,队列中的元素应该以dp[i-1]作为参考,但是队列中的元素又是我们做dp[i]的同时塞进去的。
    答: 我们不需要提前得出dp[i-1],在做dp[i]的同时维护队列是没有问题的。如果都是以dp[i-1]为参考,取队首元素更新dp[i]应该没有问题。其次,我们进队列的时候也是按照dp[i-1]为参考,所以不会有问题。
  2. 再看状态转移方程式,除数为 s [ y + 1 ] s [ x + 1 ] s[y+1]-s[x+1] ,显然可以为0。所以我们不能直接除,而要转换成乘的方式进行比较。
#include<bits/stdc++.h>
using namespace std;
#define LL long long

int dp[10009][10009];
int s[10009];

int Q[10009],L,R;

inline double check(int i,int x,int y){
    double Z=-(double)(dp[i-1][x]-dp[i-1][y]-s[y+1]*s[y+1]+s[x+1]*s[x+1]);
    return Z;
}

int main(){
    int t;scanf("%d",&t);
    int ca=0;
    while(t--){
        int n,m;scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",s+i);
        sort(s+1,s+1+n);

        for(int i=1;i<=n;i++)dp[1][i]=(s[i]-s[1])*(s[i]-s[1]);
        for(int i=2;i<=m;i++){
            Q[L=1,R=1]=i-1;//dp[i-1][i-1]
            for(int j=i;j<=n;j++){
                while(R>L){
                    double X=check(i,Q[L],Q[L+1]),Y=2.0*s[j]*(s[Q[L+1]+1]-s[Q[L]+1]);
                    if(X<=Y)L++;
                    else break;
                }
                dp[i][j]=dp[i-1][Q[L]]+(s[j]-s[Q[L]+1])*(s[j]-s[Q[L]+1]);
                while(R>L){
                    double X=check(i,Q[R-1],Q[R])*(s[j+1]-s[Q[R]+1]),Y=check(i,Q[R],j)*(s[Q[R]+1]-s[Q[R-1]+1]);
                    if(X>=Y)R--;
                    else break;
                }
                Q[++R]=j;
            }
        }
        printf("Case %d: %d\n",++ca,dp[m][n]);
    }
}

四边形优化

再来试试新学的四边形优化

d p [ i ] [ j ] = m i n ( d p [ i 1 ] [ k ] + ( s [ j ] s [ k + 1 ] ) 2 ) dp[i][j]=min(dp[i-1][k]+(s[j]-s[k+1])^2)
显然 c o s t [ j ] [ k ] = s [ j ] s [ k + 1 ] ) 2 cost[j][k]=s[j]-s[k+1])^2 满足四边形不等式 f [ a ] [ c ] + f [ b ] [ d ] &lt; = f [ b ] [ c ] + f [ a ] [ d ] ( a &lt; b &lt; = c &lt; d ) f[a][c]+f[b][d]&lt;=f[b][c]+f[a][d](a &lt; b &lt;= c&lt; d)

所以 d p dp 可以尝试四边形优化, b e s t [ i ] [ j ] [ b e s t [ i ] [ j 1 ] , b e s t [ i + 1 ] [ j ] ] best[i][j]\in[best[i][j-1],best[i+1][j]] 或者 b e s t [ i ] [ j ] [ b e s t [ i 1 ] [ j ] , b e s t [ i ] [ j + 1 ] ] best[i][j]\in[best[i-1][j],best[i][j+1]]

因为我们可以很快的得出 d p [ 1 ] dp[1] 的情况,所以 i i 应该是从小到大。所以选择 b e s t [ i ] [ j ] [ b e s t [ i 1 ] [ j ] , b e s t [ i ] [ j + 1 ] ] best[i][j]\in[best[i-1][j],best[i][j+1]] ,需要用到 j + 1 j+1 的情况,所以第二维从大到小做即可。

#include<bits/stdc++.h>
using namespace std;
#define LL long long

int dp[5009][10009];
int best[5009][10009];
int s[10009];

int main(){
    int t;scanf("%d",&t);
    int ca=0;
    while(t--){
        int n,m;scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",s+i);
        sort(s+1,s+1+n);

        for(int i=1;i<=n;i++)dp[1][i]=(s[i]-s[1])*(s[i]-s[1]),best[1][i]=1;
        for(int i=2;i<=m;i++){
            best[i][n+1]=n-1;
            //使遍历到n-1,防止遗漏
            for(int j=n;j>=i;j--){
                int now=2e9,pos;
                for(int k=best[i-1][j];k<=best[i][j+1];k++){
                    if(now>dp[i-1][k]+(s[j]-s[k+1])*(s[j]-s[k+1])){
                        now=dp[i-1][k]+(s[j]-s[k+1])*(s[j]-s[k+1]);
                        pos=k;
                    }
                }
                dp[i][j]=now;
                best[i][j]=pos;
            }
        }
        printf("Case %d: %d\n",++ca,dp[m][n]);
    }
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/90763335