1. 程式人生 > >#斜率優化,單調佇列,動態規劃#bzoj 2684 洛谷 4360 鋸木場選址

#斜率優化,單調佇列,動態規劃#bzoj 2684 洛谷 4360 鋸木場選址

題目連結

分析

d p [ i ] dp[i] 表示第二個鋸木場修到第i個位置
那麼狀態轉移方程是 d

p [ i ] = m i n ( t
o t d i s [ j ] (
j n ) ( ) ( k = 1 j w [ k ] ( ) ) d i s [ i ] ( k = j + 1 i w [ k ] ) ) ( j < i ) dp[i]=min(tot-dis[j](從j到n的距離)(字尾和)*(\sum_{k=1}^{j}w[k](字首和))-dis[i]*(\sum_{k=j+1}^{i}w[k]))(j<i)
然而這樣肯定會超時,所以必須維護一個最小的 j j ,使 d p dp 方程最優,所以說就用到了斜率優化
d p dp 方程剖析,就得到
t o t d i s [ j ] s u m [ j ] d i s [ i ] ( s u m [ i ] s u m [ j ] ) tot-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j])
把已知項移出來得到
t o t d i s [ i ] s u m [ i ] d i s [ i ] s u m [ j ] d i s [ j ] s u m [ j ] tot-dis[i]*sum[i]-dis[i]*sum[j]-dis[j]*sum[j]
若使 k ( j < k ) k(j<k) 更優秀,那麼
t o t d i s [ i ] s u m [ i ] d i s [ i ] s u m [ j ] d i s [ j ] s u m [ j ] > t o t d i s [ i ] s u m [ i ] d i s [ i ] s u m [ k ] d i s [ j ] s u m [ k ] tot-dis[i]*sum[i]-dis[i]*sum[j]-dis[j]*sum[j]>tot-dis[i]*sum[i]-dis[i]*sum[k]-dis[j]*sum[k]
最後得到 d i s [ k ] s u m [ k ] d i s [ j ] s u m [ j ] s u m [ k ] s u m [ j ] < d i s [ i ] \frac{dis[k]*sum[k]-dis[j]*sum[j]}{sum[k]-sum[j]}<dis[i]
可以發現, d i s dis 是單調遞減的,也就是維護上凸殼,用單調佇列實現


程式碼

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int d[20001],w[20001],sum,n,head=1,tail=1,q[20001],ans=2147483647;
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
    return ans;
}
inline signed min(int a,int b){return (a<b)?a:b;}
inline double slope(int x,int y){return 1.0*(d[x]*w[x]-d[y]*w[y])/(w[x]-w[y]);}
inline signed count(int x,int y){return sum-d[y]*w[y]-d[x]*(w[x]-w[y]);}
signed main(){
    n=iut();
    for (rr int i=1;i<=n;++i) w[i]=iut(),d[i]=iut();
    for (rr int i=n-1;i;--i) d[i]+=d[i+1];
    for (rr int i=1;i<=n;++i) sum+=d[i]*w[i],w[i]+=w[i-1];
    for (rr int i=1;i<=n;++i){
        while (head<tail&&slope(q[head],q[head+1])>d[i]) ++head;//找出最優的隊頭
        ans=min(ans,count(i,q[head]));
        while (head<tail&&slope(q[tail-1],q[tail])<slope(q[tail],i)) --tail;//移去不優的隊尾
        q[++tail]=i;
    }
    return !printf("%d",ans);
}