dp的斜率优化与单调队列优化

单调队列优化:

就是用一个单调队列优化dp的转移,使转移降一维。
比如入门题:
Tyvj1305:
那么dp方程是 f(i)=sum[i]minsum[k]|iMki ,注意到是取 minsum[k]|iMki 那么 sum[i] 是递增的,所以可以用一个队列优化。

还有 一题:
bzoj 1885:
我们设f[i][j] 表示到第i天手里持有j的股票的最大收益,然后转移什么的自己上网再搜,最后就维护一个就可以了。(重点不是队列,是斜率优化。)
代码:

#include
#include
#include
#define T t
const int size = 2005;
struct Day{
    int ap , as , bp , bs;
}a[size];
int T , w , maxp , f[size][size] ,q[size];
using namespace std;
int main()
{
  scanf("%d%d%d",&T,&maxp,&w);
    for(int i=1;i<=T;i++)
    {
        scanf("%d%d%d%d",&a[i].ap,&a[i].bp,&a[i].as,&a[i].bs);
    }
    memset(f,-0x3f,sizeof(f));
    for(int i=1;i<=t;i++)
    {
        for(int j=0;j<=a[i].as;j++)f[i][j]=-a[i].ap*j;
        for(int j=0;j<=maxp;j++)f[i][j]=max(f[i][j],f[i-1][j]);
        if(i-w-1>=0)
        {
            int head=0,tail=0;
            for(int j=0;j<=maxp;j++)
            {
                while(headq[head]while(head1][j]+j*a[i].ap>=f[i-w-1][q[tail-1]]+q[tail-1]*a[i].ap)tail--;
                q[tail++]=j;
                if(head1][q[head]]-(j-q[head])*a[i].ap);
            }
            head=0,tail=0;
            for(int j=maxp;j>=0;j--)
            {
                while(headq[head]>j+a[i].bs)head++;
                while(head1][j]+j*a[i].bp>=f[i-w-1][q[tail-1]]+q[tail-1]*a[i].bp)tail--;
                q[tail++]=j;
                if(head1][q[head]]+(q[head]-j)*a[i].bp);
            }
        }
    }
    int ans=0;  
    for (int i =0;i<=maxp;i++)
    ans=max(ans,f[T][i]);   
    printf("%d\n",ans); 





    return 0;
} 

斜率优化:

就是把转移的点做一个函数,用优先队列存,那么每次转移的可能点在图像上是一个渐增函数,然后每次取队列头时再搞一搞。
比如:HDU 3507

dp[i] 是取完前i个答案,然后转移方程式: dp[i]=dp[j]+(sum[i]sum[j])2 ,又设其之前有 j<k<i ,且j比k更优,那么有 dp[j]+sum[j]2(dp[k]+sum[k]2)/(2(sum[j]sum[k])<sum[i] ,则感觉像一个斜率式子。划一划得,斜率愈大愈好所以。有这样像一个斜率式子的,那么我们每次先弹队头,然后算出其dp值后再加进去,从而可以优化了。还要注意到有移项变号的问题。
代码:

#include
#include
#include
#define f(x,y,z) for (int (x);x<=y;x++)
using namespace std;
const int size = 1000; 
const int inf = 100000;
struct Ball{
    int sum,color;
}ball[size];int cnt;
int T , n ,kase=0;
int dp[size][size];
char s[size];
int main()
{
    scanf("%d",&T);
    while (    T--)
    {
        memset(s,NULL,sizeof s);
        memset(dp,0,sizeof dp);
        scanf("%s",s);    
        cnt=1;ball[1].color = (int)( s[0] -'0');ball[1].sum=1;
        for(int i=1;i<strlen(s);i++)
        if (ball[cnt].color != (int) (s[i]-'0') )  ball[++cnt]=(Ball){1,(int) (s[i]-'0')};
        else ball[cnt].sum++;

            for (int len = 0;len<=cnt;len++)
            {
                for (int i=1;i<=cnt;i++)
                {
                    int j = i + len;
                    if (j<=cnt&&1<=j)
                    {
                        dp[i][j] = inf;
                        if (i==j)
                        {
                            dp[i][j] = 3 - ball[i].sum;
                        }
                        else 
                        {
                            for (int k = i ; k <= j ; k++)
                            dp[i][j] = min(dp[i][j] , dp[i][k]+dp[k+1][j] );
                            if (ball[i].color == ball[j].color)
                            {
                                if (ball[i].sum+ball[j].sum>=3) dp[i][j] = min(dp[i][j] , dp[i+1][j-1]);
                                else dp[i][j] = min(dp[i][j] , dp[i+1][j-1] + 3- ball[i].sum-ball[j].sum); 
                                if (ball[i].sum+ball[j].sum < 4)
                                {
                                    for (int ii=i+1 ; iiif (ball[i].color==ball[ii].color && ball[ii].sum==1) 
                                    dp[i][j]=min(dp[i][j] , dp[i+1][ii-1] + dp[ii+1][j-1]);
                                }                
                            }    
                        }
                    }        
                }    
            }
     printf("Case #%d: ",++kase);
    printf("%d",dp[1][cnt]);
    putchar('\n');
    }
    return 0;
}

你可能感兴趣的:(dp)