可能是一类dp的通用优化

Description

最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务 于是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线。 新的电话线架设在已有的N(2 <= N <= 100,000)根电话线杆上, 第i根电话线杆的高度为height_i米(1 <= height_i <= 100)。 电话线总是从一根电话线杆的顶端被引到相邻的那根的顶端 如果这两根电话线杆的高度不同,那么FJ就必须为此支付 C*电话线杆高度差(1 <= C <= 100)的费用。当然,你不能移动电话线杆, 只能按原有的顺序在相邻杆间架设电话线。Farmer John认为 加高某些电话线杆能减少架设电话线的总花费,尽管这项工作也需要支出一定的费用。 更准确地,如果他把一根电话线杆加高X米的话,他得为此付出X^2的费用。 请你帮Farmer John计算一下,如果合理地进行这两种工作,他最少要在这个电话线改造工程上花多少钱。

Input

* 第1行: 2个用空格隔开的整数:N和C

* 第2..N+1行: 第i+1行仅有一个整数:height_i

Output

* 第1行: 输出Farmer John完成电话线改造工程所需要的最小花费

Sample Input

5 2
2
3
5
1
4
输入说明:
一共有5根电话线杆,在杆间拉电话线的费用是每米高度差$2。
在改造之前,电话线杆的高度依次为2,3,5,1,4米。

Sample Output

15
输出说明:
最好的改造方法是:Farmer John把第一根电话线杆加高1米,把第四根加高2米,
使得它们的高度依次为3,3,5,3,4米。这样花在加高电线杆上的钱是$5。
此时,拉电话线的费用为$2*(0+2+2+1) = $10,总花费为$15。

题目分析

最基础的转移方程

因为这里每一个元素的转移只和前一个有关系,那么自然想到$f[i][j]$表示处理到第$i$个元素,同时它的高度为$j$的最小代价。

那么总状态数是$10^5\times 10^2$,每一次转移$10^4$。正常代码不刻意卡常是无法通过的。

从数形结合看转移

写下转移方程$f[i][j]=f[i-1][k]+(j-h[i])^2+c|j-k|$发现对于同一$f[i][j]$,其每次转移是一个开口向上的二次函数,这意味着枚举前一个高度$k$时若发现代价随高度递增,那么之后状态的也不可能会更优了。

 #include<bits/stdc++.h>
#define R register int
const int maxn = ; int n,c,h[maxn],mx,ans;
int f[][],nw; inline int abs(int x){return x>?x:-x;}
int main()
{
memset(f, 0x3f3f3f3f, sizeof f);
scanf("%d%d",&n,&c);
ans = 0x3f3f3f3f;
for (R i=; i<=n; i++) scanf("%d",&h[i]), mx = h[i]<mx?mx:h[i];
for (R i=h[]; i<=mx; i++) f[][i] = (i-h[])*(i-h[]);
for (R i=; i<=n; i++)
{
for (R j=h[i]; j<=mx; j++)
{
R pre = 0x3f3f3f3f, w = (j-h[i])*(j-h[i]), val = ;
for (R k=h[i-]; k<=mx; k++)
{
val = f[nw^][k]+w+c*abs(j-k);
if (val < f[nw][j]) f[nw][j] = val;
else if (val > pre) break;
pre = val;
}
}
nw ^= ;
memset(f[nw], 0x3f3f3f3f, sizeof f[nw]);
}
for (R i=h[n]; i<=mx; i++)
ans = std::min(ans, f[nw^][i]);
printf("%d\n",ans);
return ;
}

从决策单调看转移

因为$f[i][j]=(j-h[i])^2+\{f[i-1][k]+c|j-k|\}$,而大括号内的式子与$j$无关,说明可以在枚举$j$的过程中选择最优的$k$。

至于这个选择也并不难。把式子大力拆开就是

$\begin{equation}\left\{\begin{array}{lr}f[i][j]=(j-h[i])^2+min(f[i-1][k]-c*k+c*j)\ \ (k<j) &\\ f[i][j]=(j-h[i])^2+min(f[i-1][k]+c*k-c*j)\ \ (k>j)\end{array}\right.\end{equation}$

这样就可以分别从小到大和从大到小各枚举一遍,天然保证了$j,k$之间的大小顺序。

做法来源:题解 P2885 【[USACO07NOV]电话线Telephone Wire】

#include<bits/stdc++.h>
#define R register int
const int maxn = ;
const int INF = 0x3f3f3f3f; int n,c,mx,nw,ans,h[maxn];
int f[][]; inline int min(int a, int b){return a>b?b:a;}
int main()
{
memset(f, 0x3f3f3f3f, sizeof f);
scanf("%d%d",&n,&c), ans = INF;
for (R i=; i<=n; i++) scanf("%d",&h[i]), mx = mx>h[i]?mx:h[i];
for (R i=h[]; i<=mx; i++) f[][i] = (i-h[])*(i-h[]);
for (R i=; i<=n; i++)
{
R k = INF;
for (R j=h[i-]; j<=mx; j++)
{
k = min(k, f[nw^][j]-c*j);
if (j >= h[i]) f[nw][j] = k+c*j+(j-h[i])*(j-h[i]);
}
k = INF;
for (R j=mx; j>=h[i]; j--)
{
k = min(k, f[nw^][j]+c*j);
f[nw][j] = std::min(k-c*j+(h[i]-j)*(h[i]-j), f[nw][j]);
}
nw ^= ;
memset(f[nw], 0x3f3f3f3f, sizeof f[nw]);
}
for (R i=h[n]; i<=mx; i++)
ans = min(ans, f[nw^][i]);
printf("%d\n",ans);
return ;
}

END

【动态规划】bzoj1705: [Usaco2007 Nov]Telephone Wire 架设电话线的更多相关文章

  1. bzoj1705[Usaco2007 Nov]Telephone Wire 架设电话线(dp优化)

    1705: [Usaco2007 Nov]Telephone Wire 架设电话线 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 441  Solved: ...

  2. [bzoj1705] [Usaco2007 Nov]Telephone Wire 架设电话线

    正常DP.. f[i][j]表示前i个电线杆,把第i个电线杆高度改为j的最少总费用.设原来电线杆高度为h[] f[i][j]=min{ f[i-1][k]+C*|j-k|+(j-h[i])^2,(k& ...

  3. BZOJ_1705_[Usaco2007 Nov]Telephone Wire 架设电话线_DP

    BZOJ_1705_[Usaco2007 Nov]Telephone Wire 架设电话线_DP Description 最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务 于是 ...

  4. 【bzoj1705】[Usaco2007 Nov]Telephone Wire 架设电话线 dp

    题目描述 最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务 于是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线. 新的电话线架设在已有的N(2 <= N < ...

  5. bzoj 1705;poj 3612:[Usaco2007 Nov]Telephone Wire 架设电话线

    Description 最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务 于是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线. 新的电话线架设在已有的N(2 <= ...

  6. DP+滚动数组 || [Usaco2007 Nov]Telephone Wire 架设电话线 || BZOJ 1705 || Luogu P2885

    本来是懒得写题解的…想想还是要勤发题解和学习笔记…然后就滚过来写题解了. 题面:[USACO07NOV]电话线Telephone Wire 题解: F[ i ][ j ] 表示前 i 根电线杆,第 i ...

  7. bzoj 1705: [Usaco2007 Nov]Telephone Wire 架设电话线——dp

    Description 最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务 于是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线. 新的电话线架设在已有的N(2 <= ...

  8. 【BZOJ】1705: [Usaco2007 Nov]Telephone Wire 架设电话线

    [题意]给定一排n根杆高度hi,一个常数C,杆升高x的代价为x^2,相邻两杆之间架设电话线代价为高度差*C,求总代价最小. [算法]DP+辅助数组优化 [题解]令f[i][j]表示第i根杆高度为j的最 ...

  9. BZOJ 1705: [Usaco2007 Nov]Telephone Wire 架设电话线 DP + 优化 + 推导

    Description 最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务 于是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线. 新的电话线架设在已有的N(2 <= ...

随机推荐

  1. js 检查字符串中是否包含中文(正则)

    function CheckChinese(val){ var reg = new RegExp("[\\u4E00-\\u9FFF]+","g"); if(r ...

  2. enum StatCode

    public enum StatCode { NORMAL(0,"正常"), FLAME_OUT(1,"熄火"), NOT_INSTALL(2,"未安 ...

  3. GUI的最终选择 Tkinter(八):Message组件、Spinbox组件、PanedWindow组件、Toplevel组件

    Message组件 Message(消息)组件是Label组件的变体,用于显示多行文本消息,Message组件能够自动执行,并调整文本的尺寸使其适应给定的尺寸. from tkinter import ...

  4. python 8 函数

    调用函数 Python内置了很多有用的函数,我们可以直接调用. 要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs,只有一个参数.可以直接从Python的官方网站查看文档: 也可以在交 ...

  5. fiddler手机抓包,支持前端代码调试

    手机用fiddler抓包 电脑最好是笔记本,这样能和手机保持统一局域网内:其他不多说,直接说步骤了. 一.对PC(笔记本)参数进行配置    1. 配置fiddler允许监听到https(fiddle ...

  6. go实现主线程等待子线程都运行完再退出

    方式一 package main import ( "fmt" ) func main() { ch := make(chan struct{}) count := 2 // co ...

  7. 基于JAVA的设计模式之适配器模式

    适配器模式概念 适配器模式把一个类的接口变换成客户端所期待的另一个接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作.比如我们突然就想看电影了,但是这个电影是AVI格式的,目前我们开发 ...

  8. ribbon重试机制

    我们使用Spring Cloud Ribbon实现客户端负载均衡的时候,通常都会利用@LoadBalanced来让RestTemplate具备客户端负载功能,从而实现面向服务名的接口访问. 下面的例子 ...

  9. leetcode84 Largest Rectangle in Histogram

    思路: 使用单调栈计算每个位置左边第一个比它矮的位置和右边第一个比它矮的位置即可. 实现: #include <bits/stdc++.h> using namespace std; cl ...

  10. 洛谷P3959 宝藏(模拟退火乱搞)

    题意 题目链接 题面好长啊...自己看吧.. Sol 自己想了一个退火的思路,没想到第一次交85,多退了几次就A了哈哈哈 首先把没用的边去掉,然后剩下的边从小到大排序 这样我们就得到了一个选边的序列, ...