斜率优化的动态规划题目

[HDOJ 3480.Division]

HDOJ 3480.Division
在这里插入图片描述

dp[ i ][ j ]表示把前 i 位分成 j 份。先贪心把 a 从小到大排列,花费是cost[ i ][ j ] = ( a[ j ] - a[ i ] )2
可以得到状态转移方程为f[ i ] = min { f[ j ] + ( a[ i ] - a[ j ] ) 2 | j < i }。
如果 j 比 k 更优的条件就是 f[ j ] + ( a[ i ] - a[ j + 1 ] ) 2 <= f[ k ] + ( a[ i ] - a[ k + 1 ] ) 2
将式子化简得到f[ j ] + a[ j + 1 ] 2 - f[ k ] - a[ k + 1 ] 2 <= a[ i ] * 2 * ( a[ j + 1 ] - a[ k + 1 ] )。
即 y=k*x;
如果写成 y / x <= k 的话,要注意 x 不为 0。

#include <cstdio>
#include <iostream> 
#include <algorithm>
#define maxn 10005
using namespace std;

int dp[maxn][5010];
int a[maxn],lists[maxn];

int dy(int k,int j1,int j2)
{
    return dp[j2][k]+a[j2+1]*a[j2+1]-(dp[j1][k]+a[j1+1]*a[j1+1]);
}

int dx(int j1,int j2)
{
	return 2*(a[j2+1]-a[j1+1]);
}

int main()
{
    int t;
    scanf("%d",&t);
    int index=0;
    while(t--)
    {
        index++;
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) cin >> a[i];
        if(m>=n) 
        {
            printf("Case %d: 0\n",index);
            continue;
        }
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i++) dp[i][1]=(a[i]-a[1])*(a[i]-a[1]);
        for(int j=2;j<=m;j++)
        {
            int head=1,tail=1;
            lists[tail]=0;
            for(int i=1;i<=n;i++)
            {
                while(head<tail && dy(j-1,lists[head],lists[head+1])<=a[i]*dx(lists[head],lists[head+1])) head++;
                dp[i][j]=dp[lists[head]][j-1]+(a[i]-a[lists[head]+1])*(a[i]-a[lists[head]+1]);
                while(head<tail && dy(j-1,lists[tail-1],lists[tail])*dx(lists[tail],i)>=dy(j-1,lists[tail],i)*dx(lists[tail-1],lists[tail])) tail--;
                lists[++tail]=i;
            }
        }
        printf("Case %d: %d\n",index,dp[n][m]);
    }
}

[ [HNOI2008] P3195.玩具装箱]

[HNOI2008] P3195.玩具装箱
在这里插入图片描述

dp[ i ] = min { f[ j ] + ( sum[ i ] - sum[ j ] + i - j - L - 1 ) 2 }
dp[ i ] = min { f[ j ] + ( ( sum[ i ] + i ) - ( sum[ j ] - j ) - L - 1 ) 2 }
令s[ k ] = sum[ k ] + k,L(现在) = L(原来) + 1;
展开化简移项
dp[ j ] + s[ j ] * s[ j ] - ( dp[ k ] - s[ k ] * s[ k ] ) <= 2 * ( s[ j ] - L ) * ( s[ j ] - s[ k ] )

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn=5e4+10;
int a[maxn];
long long int sum[maxn],s[maxn],dp[maxn];
int lists[maxn];

inline double Y(int j)
{
	return dp[j]+s[j]*s[j];
}

inline double X(int j)
{
	return s[j];
}

inline double slope(int j1,int j2)
{
	return (Y(j2)-Y(j1))/(X(j2)-X(j1));
}

int main()
{
	int n,L;
	cin >> n >> L;
	for(int i=1;i<=n;i++) 
	{
		int x;
		cin >> x;
		sum[i]=sum[i-1]+x;
	}
	for(int i=1;i<=n;i++) s[i]=sum[i]+i;
	L++;
	int head=1,tail=1;
	lists[1]=0;
	for(int i=1;i<=n;i++)
	{
		while(head<tail && slope(lists[head],lists[head+1])<=2.0*(s[i]-L)) head++;
		dp[i]=dp[lists[head]]+(s[i]-s[lists[head]]-L)*(s[i]-s[lists[head]]-L);
		while(head<tail && slope(lists[tail-1],lists[tail])>=slope(lists[tail],i)) tail--;
		lists[++tail]=i;
	}
	cout << dp[n] << endl;
}

[ [ZJOI2007] P2120.仓库建设]

[ZJOI2007] P2120.仓库建设
在这里插入图片描述

运到 k 处的花费:Wk = ( x[ i ] - x[ k ] ) * p[ k ];
拆开:Wk = x[ i ] * p[ k ] - x[ k ] * p[ k ];
令P[ i ] = p[ 1 ] + p[ 2 ] + … + p[ i ];
g[ i ] = - ( x[ 1 ] * p[ 1 ] ) - ( x[ 2 ] * p[ 2 ] ) - … - ( x[ i ] * p[ i ] );
dp方程: dp[ i ] = min ( dp[ j ] + x[ i ] * ( P[ i - 1 ] - P[ j ] ) + g[ i - 1 ] - g[ j ] + c[ i ] )
变形
f[ i ] + x[ i ] * P[ j ] = f[ j ] - g[ j ] + x[ i ] * P[ i - 1 ] + g[ i - 1 ] + c[ i ]
得 k = x[ i ];

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

const int maxn=1e6+10;
int a[maxn];
long long int x[maxn],p[maxn],c[maxn],g[maxn],dp[maxn];
int lists[maxn];

inline double Y(int j){return dp[j]-g[j];}
inline double X(int j){return p[j];}
inline double slope(int j1,int j2){return (Y(j2)-Y(j1))/(X(j2)-X(j1));}

int main()
{
	int n;
	cin >> n;
	for(int i=1;i<=n;i++) 
	{
		cin >> x[i] >> p[i] >> c[i];
	}
	for(int i=1;i<=n;i++)
	{
		g[i]=g[i-1]-p[i]*x[i];
		p[i]+=p[i-1];
	}
	int head=1,tail=1;
	lists[1]=0;
	for(int i=1;i<=n;i++)
	{
		while(head<tail && slope(lists[head],lists[head+1])<x[i]) head++;
		dp[i]=min(x[i]*(p[i-1])+g[i-1]+c[i],dp[lists[head]]+x[i]*(p[i-1]-p[lists[head]])+g[i-1]-g[lists[head]]+c[i]);
		while(head<tail && slope(lists[tail-1],lists[tail])>slope(lists[tail],i)) tail--;
		lists[++tail]=i;
	}
	cout << dp[n] << endl;
}

[ [APIO2010] P3628.特别行动队]

[APIO2010] P3628.特别行动队
在这里插入图片描述

dp[ i ] = max { dp[ j ] + a * ( sum[ i ] - sum[ j ] ) 2 + b * ( sum[ i ]-sum[ j ] ) + c };
sum[ i ]代表到 1 ~ i 时战斗力前缀和
化简上式得到
dp[ i ] - a * sum[ j ] 2 - b * sum[ i ] - c = dp[ j ] + a * sum[ j ] 2 - b * sum[ j ] - 2 * a * sum[ i ] * sum[ j ];
即 b = y + k * x
y = dp[ j ] + a * sum[ j ] 2 - b * sum[ j ],k = sum[ j ] , x = - 2 * a * sum[ i ];

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;

const int maxn = 1e6+10;

long long int dp[maxn],sum[maxn];
int lists[maxn];
int n;
long long int a,b,c;

inline long long int Y(int j){return dp[j]+a*sum[j]*sum[j]-b*sum[j];}
inline long long int X(int j){return sum[j];}
inline long long int slope(int j2,int j1){return (Y(j2)-Y(j1))/(X(j2)-X(j1));}

int main()
{
	cin >> n;
	cin >> a >> b >> c;
	for(int i=1;i<=n;i++) 
	{
		long long int x;
		cin >> x;
		sum[i]=sum[i-1]+x;
	}
	int head=1,tail=1;
	lists[tail]=0;
	for(int i=1;i<=n;i++)
	{
		while(head<tail && slope(lists[head+1],lists[head]) > 2*a*sum[i]) head++;
		int j=lists[head];
		dp[i]=dp[j]+a*(sum[i]-sum[j])*(sum[i]-sum[j])+b*(sum[i]-sum[j])+c;
		while(head<tail && slope(i,lists[tail]) > slope(lists[tail],lists[tail-1])) tail--;
		lists[++tail]=i;
	}
	cout << dp[n] << endl;
	return 0;
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值