kuangbin 斜率优化题单 代码

73 篇文章 0 订阅

题单链接
https://vjudge.net/article/752

推荐:1,2,5

1. HDU-3507 Print Article

f i = min ⁡ 0 ≤ j < i { f j + ( s i − s j ) 2 + M } f_i = \min_{0 \le j < i} \{ f_j + (s_i - s_j)^2 + M \} fi=0j<imin{fj+(sisj)2+M}

// https://vjudge.net/problem/HDU-3507
#include <cstdio>
using namespace std;
/*
fi = min{ fj + (si-sj)^2 + m }

k<j<i
j better than k

fk + (si-sk)^2 + m > fj + (si-sj)^2 + m
fk + sk^2 - 2*si*sk > fj + sj^2 - 2*si*sj
sj^2 - sk^2 + fj - fk < si*2*(sj-sk)
((sj^2+fj)-(sk^2+fj)) / (2*sj - 2*sk) < si

xi = 2*si
yi = si^2 + fi
*/

#define MAXN 500005

int n, m;

long long c[MAXN];
long long s[MAXN];
long long x[MAXN], y[MAXN];
long long f[MAXN];

int q[MAXN], ql, qr;

bool solve()
{
    if (scanf("%d%d", &n, &m)!=2) return false;
    for (int i=1; i<=n; i++) {
        scanf("%lld", &c[i]);
        s[i] = s[i-1]+c[i];
    }

    ql=1;
    qr=0;
    q[++qr] = 0;
    for (int i=1; i<=n; i++) {

        while (ql<qr && (y[q[ql+1]] - y[q[ql]]) <= s[i]*(x[q[ql+1]] - x[q[ql]]))    // 注意要加上等于号
            ql++;
        f[i] = f[q[ql]] + (s[i] - s[q[ql]])*(s[i] - s[q[ql]]) + m;
        x[i] = 2*s[i];
        y[i] = s[i]*s[i] + f[i];

        while (ql<qr && ((y[i] - y[q[qr]])*(x[q[qr]]-x[q[qr-1]]) <= (y[q[qr]]-y[q[qr-1]])*(x[i]-x[q[qr]])))
            qr--;
        q[++qr] = i;
    }

    printf("%lld\n", f[n]);
    return true;
}


int main()
{
    while (solve());
    return 0;
}


/*

5 5
5 9 5 7 5

*/

2. HDU-2829 Lawrence

f m , i = min ⁡ m < j < i { f j + ( s i − s j ) 2 − ( t i − t j ) 2 } f_{m,i} = \min_{m < j < i} \{ f_j + \frac{(s_i - s_j)^2 - (t_i - t_j)}{2} \} fm,i=m<j<imin{fj+2(sisj)2(titj)}

#include <cstdio>

/*
s[k] = Sum[ai, {i, 1, k}]
t[k] = Sum[ai^2, {i, 1, k}]

fi = min{ fj + ((si-sj)^2 - (ti-tj)) / 2 }

k<j
j better than k
fj + ((si-sj)^2 - (ti-tj))/2 < fk + ((si-sk)^2 - (ti-tk))/2
((2fj + sj^2 + tj) - (2fk + sk^2 + tk)) / (2sj - 2sk) < si

yi = 2fi + si^2 + ti
xi = 2si

(k, j) < (j, i)
*/

#define MAXN 1000

int n, m;
long long s[MAXN];
long long t[MAXN];
long long dp[MAXN][MAXN];   // ans = dp[m+1][n]

int q[MAXN], ql, qr;

long long get_up(int k, int j, long long *f)
{
    return (2*f[j] + s[j]*s[j] + t[j]) - (2*f[k] + s[k]*s[k] + t[k]);
}

long long get_down(int k, int j)
{
    return ((s[j]-s[k])<<1);
}


bool solve()
{
    int tmp;
    scanf("%d%d", &n, &m);
    if ((n|m) == 0) return false;
    for (int i=1; i<=n; i++) {
        scanf("%d", &tmp);
        s[i] = s[i-1] + tmp;
        t[i] = t[i-1] + tmp*tmp;
    }

    for (int i=1; i<=n; i++) {
        dp[1][i] = (s[i]*s[i]-t[i])/2;
    }

    for (int mm=2, maxmm=m+1; mm<=maxmm; mm++) {
        ql = 1;
        qr = 0;
        q[++qr] = mm-1;     // 注意前面至少有 mm-1 个,所以一开始入队列的是 mm-1

        for (int i=mm; i<=n; i++) {
            while (ql<qr && get_up(q[ql], q[ql+1], dp[mm-1]) <= get_down(q[ql], q[ql+1])*s[i])
                ql++;
            dp[mm][i] = dp[mm-1][q[ql]] + ((s[i]-s[q[ql]])*(s[i]-s[q[ql]]) - (t[i]-t[q[ql]]))/2;

            while (ql<qr && get_up(q[qr-1], q[qr], dp[mm-1])*get_down(q[qr], i) >= get_up(q[qr], i, dp[mm-1])*get_down(q[qr-1], q[qr]))
                qr--;
            q[++qr] = i;
        }
    }

    printf("%lld\n", dp[m+1][n]);

    return true;
}


int main()
{
    while (solve());
    return 0;
}


/*

4 1
4 5 1 2

4 2
4 5 1 2

0 0

17 2


20 5
23 95 77 90 77 69 24 84 73 9 37 11 28 33 42 39 64 18 91 6 
49824

7 3
65 63 99 3 31 16 84 
4732

10 6
63 97 45 93 42 82 94 8 40 9 
4196

10 8
37 71 3 16 74 91 5 40 40 49 
48

5 3
5 4 3 2 1


*/

4. HDU-1300 Pearls

f i = min ⁡ 0 ≤ j < i { f j + ( s i − s j + 10 ) ∗ p i } f_i = \min_{0 \le j < i} \{ f_j + (s_i - s_j + 10)*p_i \} fi=0j<imin{fj+(sisj+10)pi}

#include <cstdio>
/*
p[i] < p[i+1]

si = a1+a2+...+ai
fi = min{ fj + (si-sj+10)*mxp[j+1..i] }
fi = min{ fj + (si-sj+10)*p[i]}

k<j<i
j better than k
fj + (si-sj+10)*pi < fk + (si-sk+10)*pi
fj-sj*p[i] < fk-sk*p[i]
(fj-fk)/(sj-sk) < pi

yi = fi
xi = si

(k, j) < (j, i)
 
*/

#define MAXN 200

int n;
long long a[MAXN];
long long p[MAXN];
long long s[MAXN];
long long f[MAXN];

long long q[MAXN], ql, qr;

long long get_up(int k, int j)
{
    return f[j]-f[k];
}

long long get_down(int k, int j)
{
    return s[j]-s[k];
}

void solve()
{
    scanf("%d", &n);
    for (int i=1; i<=n; i++) {
        scanf("%lld%lld", &a[i], &p[i]);
        s[i] = s[i-1]+a[i];
    }

    ql=1;
    qr=0;
    q[++qr] = 0;
    for (int i=1; i<=n; i++) {
        while (ql<qr && get_up(q[ql], q[ql+1]) <= p[i] * get_down(q[ql], q[ql+1]))
            ql++;
        f[i] = f[q[ql]] + (s[i]-s[q[ql]]+10)*p[i];

        while (ql<qr && get_up(q[qr-1], q[qr])*get_down(q[qr], i) >= get_up(q[qr], i)*get_down(q[qr-1], q[qr]))
            qr--;
        q[++qr] = i;
    }

    printf("%lld\n", f[n]);
}


int main()
{
    int ttt;
    scanf("%d", &ttt);
    while (ttt--) {
        solve();
    }
    return 0;
}

/*

2
100 1
100 2
330

3
1 10
1 11
100 12
1344

5
6 3
4 3
4 5
9 9
8 9
339
*/

5. HDU-2993 MAX Average Problem

这题卡读入优化有点骚
这题是纯斜率的,和前面几题有点不同,不过也没有差太多。可以说是另一类板子题吧。
https://blog.csdn.net/qq_36294918/article/details/103641411
f i = max ⁡ 0 ≤ j ≤ i − k { s i − s j i − j } f_i = \max_{0 \le j \le i-k} \{ \frac{s_i - s_j}{i - j} \} fi=0jikmax{ijsisj}

#include <cstdio>

/*
fi = max{ (si-sj)/(i-j) , 0<=j<=i-k }

k<j<i
j better than k
(si-sj)/(i-j) > (si-sk)/(i-k)
(si-sj)*(i-k) > (si-sk)*(i-j)

*/

#define MAXN 100005

int n, m;
int a[MAXN];
int s[MAXN];
int f[MAXN];
int q[MAXN], ql, qr;
double ans;


inline char __getchar()
{
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline bool qread(int &x)
{
	char ch=__getchar();
	if(ch==EOF)return false;
	x=0;
	while(!(ch>='0'&&ch<='9'))ch=__getchar();
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=__getchar();
    return x;
}

double max(const double &x, const double &y) {return x<y?y:x;}
double get_k(int x, int y) {return 1.0*(s[y]-s[x])/(y-x);}

int solve()
{
    if (!qread(n) || !qread(m)) return false;
    for (int i=1; i<=n; i++) qread(a[i]), s[i] = s[i-1]+a[i];

    ql=1;
    qr=0;
    ans=0;

    for (int i=m, ii=0; i<=n; i++, ii++) {
        while (ql<qr && get_k(q[qr-1], q[qr])>=get_k(q[qr], ii))
            qr--;
        q[++qr] = ii;

        while (ql<qr && get_k(q[ql], i)<=get_k(q[ql+1], i))
            ql++;
        
        ans = max(ans, get_k(q[ql], i));
    }

    printf("%.2f\n", ans);

    return true;
}


int main()
{
    while (solve());
    return 0;
}

/*
10 6
6 4 2 10 3 8 5 9 4 1
6.50
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值