【四连测】——Telephone Wire


QAQ

题目描述

洛谷原题
数据弱化版(加可见)

分析

状态分析

发现有三个量:

  1. 电线杆
  2. 该电线杆的高度
  3. 当时的最小费用

所以令 d p i , j dp_{i,j} dpi,j表示第 i i i杆电线杆高度为 j j j时所花的最小费用即可

暴力DP分析

分析: 0 0 0
状态转移方程
d p i , j = m i n { d p i − 1 , k + c ∗ ∣ j − k ∣ + ( j − h i ) 2 } dp_{i,j}=min\{dp_{i-1,k}+c*|j-k|+(j-h_i)^2\} dpi,j=min{dpi1,k+cjk+(jhi)2}
嗯,枚举一下当前电线杆的高度( j j j)和前一根电线杆的高度( k k k)即可

优化分析

上一个的复杂度是 O ( n ∗ H m a x 2 ) O(n*{H_{max}}^2) O(nHmax2)
H m a x = 100 H_{max}=100 Hmax=100 n = 100000 n=100000 n=100000,不爆才怪……(好像LGOJ评测机很好的样子)
首先, j − h i j-h_i jhi是一个定值,所以可以暂时不管
然后,看到有一个 ∣ j − h i ∣ |j-h_i| jhi,绝对值,数学遇到了该怎么办?分类讨论。
所以
d p i , j = m i n { d p i − 1 , k + c ∗ k } − c ∗ j + ( j − h i ) 2 ( k > = j ) dp_{i,j}=min\{dp_{i-1,k}+c*k\}-c*j+(j-h_i)^2(k>=j) dpi,j=min{dpi1,k+ck}cj+(jhi)2(k>=j)
d p i , j = m i n { d p i − 1 , k − c ∗ k } + c ∗ j + ( j − h i ) 2 ( k &lt; j ) dp_{i,j}=min\{dp_{i-1,k}-c*k\}+c*j+(j-h_i)^2(k&lt;j) dpi,j=min{dpi1,kck}+cj+(jhi)2(k<j)
诶?枚举 k k k时, j j j为一个定值。这样一来, j , k j,k j,k就可以同时枚举!
搞一个预处理,显然 d p 1 , j = ( j − h 1 ) 2 dp_{1,j}=(j-h_1)^2 dp1,j=(jh1)2,于是,下面就可以通过 j , k j,k j,k同时枚举而优化,关键代码如下

for i 2 to n {
    long long Minn=INF,Minn1=INF;
    for j 0 to h[i]
        Minn=Min dp[i-1][j]-c*j;
    for j h[i] to maxh
        Minn=Min dp[i-1][j]-c*j;
        dp[i][j]=Min Minn+c*j+(j-h[i])*(j-h[i]);
    for j 100 downto j>=h[i] {
        Minn1=Min dp[i-1][j]+c*j;
        dp[i][j]=Min Minn1-c*j+(j-h[i])*(j-h[i]);
    }
}

End…

代码

暴力+ O 2 O2 O2靠LG强评测机能过的代码

// luogu-judger-enable-o2
#include<map>
#include<set>
#include<list>
#include<queue>
#include<deque>
#include<stack>
#include<ctime>
#include<cmath>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cctype>
#include<string>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<iomanip>
#include<iostream>
#include<algorithm>
using namespace std;
#define reg register
template <typename T>
inline T read() {
    T a=0; char c=getchar(),f=1;
    while(c<'0'||c>'9') {
        if(c=='-') f=-f;
        if(c==-1) return c;
        c=getchar();
    }
    while(c>='0'&&c<='9') a=(a<<1)+(a<<3)+(c^48),c=getchar();
    return a*f;
}
template <class T>
inline int write(T x) {
    if(x<0) x=(~x)+1, putchar('-');
    if(x/10) write(x/10);
    return putchar(x%10|48);
}
template <class T>
inline int write(T x,char c) {
    return write(x)&&putchar(c);
}
template <class T>
inline T Max(T a,T b) { return a>b?a:b; }
template <class T>
inline T Min(T a,T b) { return a<b?a:b; }
template <class T>
inline T Abs(T a) { return a<0?-a:a; }
int n=read<int>(),c=read<int>();
long long h[100001];
long long dp[100001][101];
long long ans=0x3f3f3f3f;
long long mh;
int main() {
    for(reg int i=1;i<=n;i++)
        mh=Max(mh,h[i]=read<long long>());
    memset(dp,0x3f,sizeof dp);
    for(reg int i=0;i<=mh;i++)
        dp[1][i]=(i-h[1])*(i-h[1]);
    for(reg int i=2;i<=n;i++) {
        for(reg int j=h[i];j<=mh;j++) {
            for(reg int k=h[i-1];k<=mh;k++) {
                dp[i][j]=Min(dp[i][j],dp[i-1][k]+c*Abs(j-k)+(j-h[i])*(j-h[i]));
            }
        }
    }
    for(reg int i=1;i<=mh;i++) {
        ans=Min(ans,dp[n][i]);
    }
    write(ans);
}

正解代码

#include<cstdio>
#include<cstring>
using namespace std;
#define reg register
template <typename T>
inline T read() {
    T a=0; char c=getchar(),f=1;
    while(c<'0'||c>'9') {
        if(c=='-') f=-f;
        if(c==-1) return c;
        c=getchar();
    }
    while(c>='0'&&c<='9') a=(a<<1)+(a<<3)+(c^48),c=getchar();
    return a*f;
}
template <class T>
inline int write(T x) {
    if(x<0) x=(~x)+1, putchar('-');
    if(x/10) write(x/10);
    return putchar(x%10|48);
}
template <class T>
inline int write(T x,char c) {
    return write(x)&&putchar(c);
}
template <class T>
inline T Min(T a,T b) { return a<b?a:b; }
const long long INF=1e15;
int n=read<int>(),c=read<int>();
long long h[100001];
long long dp[100001][101];
long long ans=INF;
long long mh;
int main() {
    for(reg int i=1;i<=n;i++)
        h[i]=read<long long>();
    memset(dp,0x3f,sizeof dp);
    for(reg int i=h[1];i<=100;i++)
        dp[1][i]=(i-h[1])*(i-h[1]);
    for(reg int i=2;i<=n;i++) {
        long long Minn=INF,Minn1=INF;
        for(int j=0;j<=h[i];j++)
            Minn=Min((dp[i-1][j]-c*j),Minn);
        for(int j=h[i];j<=100;j++) {
            Minn=Min(Minn,dp[i-1][j]-c*j);
            dp[i][j]=Min(dp[i][j],Minn+c*j+(j-h[i])*(j-h[i]));
        }
        for (int j=100;j>=h[i];j--) {
            Minn1=Min(Minn1,(dp[i-1][j]+c*j));
            dp[i][j]=Min(dp[i][j],Minn1-c*j+(j-h[i])*(j-h[i]));
        }
    }
    for(reg int i=1;i<=100;i++)
        ans=Min(ans,dp[n][i]);
    write(ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值