hdu3507
◦要输出N个数字a[N],输出的时候可以连续的输出,每连续输出一串,它
的费用是 :这串数字和的平方加上一个常数M。求最小费用。
◦0 ≤ n ≤ 500000, 0 ≤ M ≤ 1000
思路
- dp[i]表示取到i的最小费用
dp[i]=min{dp[j]+(sum[i]-sum[j])^2+M} (j<i)
O(N^2) - 斜率
- 维护下凸包
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=500100;
int n,M;
int a[N],f[N],sum[N];
int q[N];
int getup(int j,int k)
{
return f[j]+sum[j]*sum[j]-f[k]-sum[k]*sum[k];
}
int getdown(int j,int k)//j>k
{
return 2*(sum[j]-sum[k]);
}
int getdp(int i,int j)
{
return f[j]+(sum[i]-sum[j])*(sum[i]-sum[j])+M;
}
int main()
{
int head,tail;
while(scanf("%d%d",&n,&M)!=EOF)
{
sum[0]=0;
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
head=tail=1;
q[1]=0;
for(int i=1;i<=n;i++)
{
while(head<tail&&getup(q[head+1],q[head])
<=sum[i]*getdown(q[head+1],q[head]))head++;
f[i]=getdp(i,q[head]);
while(head<tail&&getup(i,q[tail])*getdown(q[tail],q[tail-1])
<=getup(q[tail],q[tail-1])*getdown(i,q[tail])) tail--;
q[++tail]=i;
}
cout<<f[n]<<endl;
}
return 0;
}
bzoj 3156
思路:
- dp[i]表示第i个位置放守护塔的最小收益,所以我们在加一个a[n+1]=0,答案就是dp[n+1]了
- dp[i]=min{ (dp[j]+(i-j-1)*(i-j)/2+a[i] }
- 这个式子显然可以效率优化
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stdio.h>
#define ll long long
using namespace std;
const int N=1e6+100;
int n;
ll a[N],f[N];
int q[N];
ll getup(ll j,ll k)
{
return 2*f[j]-2*f[k]-k*k-k+j*j+j;
}
ll getdown(ll j,ll k)//j>k
{
return 2*(j-k);
}
ll getdp(ll i,ll j)
{
return f[j]+(ll)(i-j-1)*(i-j)/2+a[i];
}
int main()
{
int head,tail;
int n;
cin>>n;
for(int i=n;i>=1;i--) scanf("%lld",&a[i]);
a[n+1]=0;
head=tail=1;
q[1]=1;
f[1]=a[1];
for(int i=2;i<=n+1;i++)
{
while(head<tail&&getup(q[head+1],q[head])
<=i*getdown(q[head+1],q[head]))head++;
f[i]=getdp(i,q[head]);
while(head<tail&&getup(i,q[tail])*getdown(q[tail],q[tail-1])
<=getup(q[tail],q[tail-1])*getdown(i,q[tail])) tail--;
q[++tail]=i;
}
cout<<f[n+1];
return 0;
}