就是用一个单调队列优化dp的转移,使转移降一维。
比如入门题:
Tyvj1305:
那么dp方程是 f(i)=sum[i]−minsum[k]|i−M≤k≤i ,注意到是取 −minsum[k]|i−M≤k≤i 那么 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;
}