【题目链接】
ybt 1609:【例 4】Cats Transport
洛谷 CF311B Cats Transport
【题目考点】
1. 动态规划:斜率优化动规
【解题思路】
解法1:设a点的前缀和
输入的 d d d序列是从 d 2 d_2 d2到 d n d_n dn,共n-1个数字。人为设 d 1 = 0 d_1=0 d1=0。
设 d d d序列的前缀和数组 s s s,自然 s 1 = 0 s_1=0 s1=0。
思考:一只猫在玩完后,刚好有个饲养员走到这只猫所在的山,将它接走,那么这名饲养员应该从哪一时刻出发?
设 a i a_i ai表示第 i i i只猫在时刻 t i t_i ti被一名饲养员接走,接走它的饲养员的出发时刻。
从第1座山走到第 h i h_i hi座山的距离为 ∑ x = 2 h i d x = s h i − s 1 = s h i \sum_{x=2}^{h_i}d_x = s_{h_i}-s_1=s_{h_i} ∑x=2hidx=shi−s1=shi。由于饲养员的速度为1米每单位时间,那么从第1座山走到第 h i h_i hi座山花费的时间为 s h i s_{h_i} shi,饲养员在 t i t_i ti时刻到达第 h i h_i hi座山,那么饲养员的出发时间 a i = t i − s h i a_i = t_i-s_{h_i} ai=ti−shi
将所有猫的 a i a_i ai从小到大排序。
如果饲养员在 a i + x a_i+x ai+x时刻出发,会在 t i + x t_i+x ti+x时刻接到第 i i i只猫,此时第 i i i只猫等待了 x x x个单位的时间。
现在设想一个表示时间的数轴,将每只猫的 a i a_i ai标在数轴上,将这样的点叫做 a a a类点。各饲养员的出发时刻标记在数轴上,记为 b 1 , . . . , b p b_1,...,b_p b1,...,bp,这样的点叫做 b b b类点。
设 b l b_l bl和 b r b_r br是相邻的两个 b b b类点,那么所有满足 b l < a i ≤ b r b_l< a_i \le b_r bl<ai≤br的 a a a类点对应的猫都会被在 b r b_r br时刻出发的饲养员接走,第 i i i只猫的等待时长为 b r − a i b_r-a_i br−ai。将猫的等待时间称为代价,因此也可以说 a i a_i ai点贡献的代价为 b r − a i b_r-a_i br−ai。
要想使代价和最小, b b b点一定选择要在某 a a a点上,而不会选择在两个 a a a点之间。
如果 b l < a x ≤ . . . ≤ a y < b r < a y + 1 b_l<a_x\le...\le a_y<b_r<a_{y+1} bl<ax≤...≤ay<br<ay+1,则 b l b_l bl和 b r b_r br之间 a a a点的代价和为: ∑ b l < a i ≤ b r ( b r − a i ) \underset{b_l< a_i \le b_r}\sum(b_r-a_i) bl<ai≤br∑(br−ai)
将 b r b_r br点调整到 a y a_y ay, b r b_r br减小,代价和 ∑ b l < a i ≤ b r ( b r − a i ) \underset{b_l< a_i \le b_r}\sum(b_r-a_i) bl<ai≤br∑(br−ai)会减小。
a a a点序列为 a 0 , a 1 , . . . , a m a_0,a_1, ..., a_m a0,a1,...,am,人为添加一个点 a 0 = 0 a_0=0 a0=0
设 b b b点选择的位置为 a b 1 , a b 2 , . . . , a b p a_{b_1}, a_{b_2},...,a_{b_p} ab1,ab2,...,abp,设 b 0 = 0 b_0=0 b0=0。
最后一个选择的点 a b p a_{b_p} abp必然是 a m a_m am,即 b p = m b_p=m bp=m。
如果 b p < m b_p<m bp<m,则 a m a_m am对应的猫不会被饲养员接走。如果 b p > m bp>m bp>m,不如 b p = m bp=m bp=m代价更小。
可以设 a a a序列的前缀和 s u m A sumA sumA, s u m A i = ∑ x = 1 i a x sumA_i = \sum_{x=1}^ia_x sumAi=∑x=1iax
已知 a l < a i ≤ a r a_l < a_i \le a_r al<ai≤ar范围内有 r − l r-l r−l个 a a a点,该范围内 a a a点的加和 ∑ a l < a i ≤ a r a i = s u m A r − s u m A l \underset{a_l<a_i\le a_r}\sum a_i = sumA_r-sumA_l al<ai≤ar∑ai=sumAr−sumAl。
满足 a l < a i ≤ a r a_l<a_i\le a_r al<ai≤ar的 a a a点的代价加和为 ∑ a l < a i ≤ a r ( a r − a i ) = ( r − l ) a r − ( s u m A r − s u m A l ) \underset{a_l< a_i \le a_r}\sum(a_r-a_i)=(r-l)a_r-(sumA_r-sumA_l) al<ai≤ar∑(ar−ai)=(r−l)ar−(sumAr−sumAl)
状态定义
- 阶段:前 j j j个 a a a点中选择 i i i个点
前 j j j个 a a a点对应的猫都应该被接走,因此必须选择最后一个 a a a点 a j a_j aj作为 b b b点 - 决策:选择哪个 a a a点作为下一个 b b b点。
- 策略:选择点的方案
- 策略集合:在前 j j j个 a a a点中选择 i i i个点的所有方案
- 条件:代价最小
- 统计量:代价
状态定义 d p i , j dp_{i,j} dpi,j:在前 j j j个 a a a点中选择 i i i个点,且一定选择 a j a_j aj点的所有方案中,代价最小的方案的代价。
初始状态:
当 i ≥ j i\ge j i≥j时,可以选择每个 a a a点都作为 b b b点,代价 d p i , j dp_{i,j} dpi,j为0。所以 d p i , 0 = 0 , 0 ≤ i ≤ p dp_{i,0}=0, 0\le i \le p dpi,0=0,0≤i≤p。
当 i < j i<j i<j且 i = 0 i=0 i=0时,有猫但没有饲养员,是不符合条件的,因此设 d p 0 , j = inf , 0 < j ≤ m dp_{0,j}=\inf,0<j\le m dp0,j=inf,0<j≤m
状态转移方程
- 策略集合:在前 j j j个 a a a点中选择 i i i个点,且选择 a j a_j aj点的所有方案
- 分割策略集合:由于第 i i i个选点一定选择 a j a_j aj点,那么根据第 i − 1 i-1 i−1个选择的点分割策略集合。
设选择的第 i − 1 i-1 i−1个点为 a h a_h ah,选择 a a a点的最小值为 a 0 a_0 a0,也就是没有上一个选点,前 i − 1 i-1 i−1个饲养员不接猫。选择的 a a a点最大为 a j − 1 a_{j-1} aj−1,所以 0 ≤ h < j 0\le h < j 0≤h<j
要想在 a 0 ∼ a j a_0\sim a_j a0∼aj中选择 i i i个点且包括 a j a_j aj,使代价最小。那么首先在 a 0 ∼ a h a_0\sim a_h a0∼ah中选择 i − 1 i-1 i−1个点且包括 a h a_h ah,可以获得的最小代价为 d p i − 1 , h dp_{i-1,h} dpi−1,h。
a h a_h ah的下一个选择的点为 a j a_j aj, a h < a x ≤ a j a_h<a_x\le a_j ah<ax≤aj中的 a x a_x ax产生代价 ∑ x = h + 1 j ( a j − a x ) = ( j − h ) a j − ( s u m A j − s u m A h ) = ( j − h ) a j + s u m A h − s u m A j \sum_{x=h+1}^j(a_j-a_x)=(j-h)a_j-(sumA_j-sumA_h)=(j-h)a_j+sumA_h-sumA_j ∑x=h+1j(aj−ax)=(j−h)aj−(sumAj−sumAh)=(j−h)aj+sumAh−sumAj。
对于不同的 h h h的取值,求代价的最小值。
因此状态转移方程为: d p i , j = m i n { d p i − 1 , h + ( j − h ) a j + s u m A h − s u m A j } , 0 ≤ h < j dp_{i,j}=min\{dp_{i-1,h}+(j-h)a_j+sumA_h-sumA_j\},0\le h < j dpi,j=min{dpi−1,h+(j−h)aj+sumAh−sumAj},0≤h<j
进行斜率优化:
方程中去掉 m i n min min,将与 h h h相关的当做变量,与整理方程
− d p i − 1 , h − s u m A h = j ⋅ a j − h ⋅ a j − d p i , j − s u m A j -dp_{i-1,h}-sumA_h=j\cdot a_j-h\cdot a_j-dp_{i,j}-sumA_j −dpi−1,h−sumAh=j⋅aj−h⋅aj−dpi,j−sumAj
d p i − 1 , h + s u m A h = a j ⋅ h + d p i , j − j ⋅ a j + s u m A j dp_{i-1,h}+sumA_h=a_j\cdot h+dp_{i,j}-j\cdot a_j+sumA_j dpi−1,h+sumAh=aj⋅h+dpi,j−j⋅aj+sumAj
设 y = d p i − 1 , h + s u m A h y=dp_{i-1,h}+sumA_h y=dpi−1,h+sumAh, x = h x=h x=h, k = a j k=a_j k=aj, b = d p i , j − j ⋅ a j + s u m A j b=dp_{i,j}-j\cdot a_j+sumA_j b=dpi,j−j⋅aj+sumAj
则该方程就是直线方程 y = k x + b y=kx+b y=kx+b,决策点为 ( h , d p i − 1 , h + s u m A h ) (h, dp_{i-1,h}+sumA_h) (h,dpi−1,h+sumAh),看直线过哪个决策点时截距 b b b最小,就可以求出 d p i , j dp_{i,j} dpi,j。
斜率 a j a_j aj随着 j j j的增大而增大,因此可以进行队头出队,取队头即为最优决策点。
斜率优化的具体原理、和代码细节不再赘述,参考模板题:
信息学奥赛一本通 1607:【 例 2】任务安排 2 | 洛谷 P10979 任务安排 2
求出的 p p p个饲养员接 m m m只猫的最小代价 d p p , m dp_{p,m} dpp,m即为最终结果
解法2:最后减 a a a点加和
a a a点序列为 a 0 , a 1 , . . . , a m a_0,a_1, ..., a_m a0,a1,...,am,人为添加一个点 a 0 = 0 a_0=0 a0=0
设 b b b点选择的位置为 a b 1 , a b 2 , . . . , a b p a_{b_1}, a_{b_2},...,a_{b_p} ab1,ab2,...,abp,设 b 0 = 0 b_0=0 b0=0。
最后一个选择的点 a b p a_{b_p} abp必然是 a m a_m am,即 b p = m b_p=m bp=m。
已知 a l < a i ≤ a r a_l < a_i \le a_r al<ai≤ar范围内有 r − l r-l r−l个 a a a点。
- 满足 a b 0 < a i ≤ a b 1 a_{b_0}<a_i\le a_{b_1} ab0<ai≤ab1的 a a a点的代价加和为 ∑ a b 0 < a i ≤ a b 1 ( a b 1 − a i ) = b 1 a b 1 − ∑ 0 < a i ≤ a b 1 a i \underset{a_{b_0}< a_i \le a_{b_1}}\sum(a_{b_1}-a_i)=b_1a_{b_1}-\underset{0< a_i \le a_{b_1}}\sum a_i ab0<ai≤ab1∑(ab1−ai)=b1ab1−0<ai≤ab1∑ai
- 满足 a b 1 < a i ≤ a b 2 a_{b_1}<a_i\le a_{b_2} ab1<ai≤ab2的 a a a点的代价加和为 ∑ a b 1 < a i ≤ a b 2 ( a b 2 − a i ) = ( b 2 − b 1 ) a b 2 − ∑ a b 1 < a i ≤ a b 2 a i \underset{a_{b_1}< a_i \le a_{b_2}}\sum(a_{b_2}-a_i)=(b_2-b_1)a_{b_2}-\underset{a_{b_1}< a_i \le a_{b_2}}\sum a_i ab1<ai≤ab2∑(ab2−ai)=(b2−b1)ab2−ab1<ai≤ab2∑ai
- …
- 满足 a b p − 1 < a i ≤ a b p a_{b_{p-1}}<a_i\le a_{b_p} abp−1<ai≤abp的 a a a点的代价加和为 ∑ a b p − 1 < a i ≤ a b p ( a b p − a i ) = ( b p − b p − 1 ) a b p − ∑ a b p − 1 < a i ≤ a b p a i \underset{a_{b_{p-1}}< a_i \le a_{b_p}}\sum(a_{b_p}-a_i)=(b_p-b_{p-1})a_{b_p}-\underset{a_{b_{p-1}}< a_i \le a_{b_p}}\sum a_i abp−1<ai≤abp∑(abp−ai)=(bp−bp−1)abp−abp−1<ai≤abp∑ai
总代价和为:
∑ i = 1 p ( b i − b i − 1 ) a b i − ∑ i = 1 b p a i \sum_{i=1}^p(b_i-b_{i-1})a_{b_i}-\sum_{i=1}^{b_p}a_i ∑i=1p(bi−bi−1)abi−∑i=1bpai
后面 ∑ i = 1 b p a i = ∑ i = 1 m a i \sum_{i=1}^{b_p}a_i=\sum_{i=1}^ma_i ∑i=1bpai=∑i=1mai是定值,记为 s u m A sumA sumA,在动规过程中不用考虑。
只需要考虑不同选择点的方案中,哪种方案的代价 ∑ i = 1 p ( b i − b i − 1 ) a b i \sum_{i=1}^p(b_i-b_{i-1})a_{b_i} ∑i=1p(bi−bi−1)abi是最小的。对于相邻选择的点 a b i − 1 a_{b_{i-1}} abi−1和 a b i a_{b_i} abi,贡献的代价为 ( b i − b i − 1 ) a b i (b_i-b_{i-1})a_{b_i} (bi−bi−1)abi。
状态定义
状态定义 d p i , j dp_{i,j} dpi,j:在前 j j j个 a a a点中选择 i i i个点,且一定选择 a j a_j aj点的所有方案中,代价最小的方案的代价。
初始状态:
当 i ≥ j i\ge j i≥j时,可以选择每个 a a a点都作为 b b b点,代价 d p i , j dp_{i,j} dpi,j为0。所以 d p i , 0 = 0 , 0 ≤ i ≤ p dp_{i,0}=0, 0\le i \le p dpi,0=0,0≤i≤p。
当 i < j i<j i<j且 i = 0 i=0 i=0时,有猫但没有饲养员,是不符合条件的,因此设 d p 0 , j = inf , 0 < j ≤ m dp_{0,j}=\inf,0<j\le m dp0,j=inf,0<j≤m
状态转移方程
- 策略集合:在前 j j j个 a a a点中选择 i i i个点,且选择 a j a_j aj点的所有方案
- 分割策略集合:由于第 i i i个选点一定选择 a j a_j aj点,那么根据第 i − 1 i-1 i−1个选择的点分割策略集合。
设选择的第 i − 1 i-1 i−1个点为 a h a_h ah,选择 a a a点的最小值为 a 0 a_0 a0,也就是没有上一个选点,前 i − 1 i-1 i−1个饲养员不接猫。选择的 a a a点最大为 a j − 1 a_{j-1} aj−1,所以 0 ≤ h < j 0\le h < j 0≤h<j
要想在 a 0 ∼ a j a_0\sim a_j a0∼aj中选择 i i i个点且包括 a j a_j aj,使代价最小。那么首先在 a 0 ∼ a h a_0\sim a_h a0∼ah中选择 i − 1 i-1 i−1个点且包括 a h a_h ah,可以获得的最小代价为 d p i − 1 , h dp_{i-1,h} dpi−1,h。
a h a_h ah的下一个选择的点为 a j a_j aj, a h < a x ≤ a j a_h<a_x\le a_j ah<ax≤aj中的 a x a_x ax产生代价 ( j − h ) a j (j-h)a_j (j−h)aj。
对于不同的 h h h的取值,求代价的最小值。
因此状态转移方程为: d p i , j = m i n { d p i − 1 , h + ( j − h ) a j } , 0 ≤ h < j dp_{i,j}=min\{dp_{i-1,h}+(j-h)a_j\},0\le h < j dpi,j=min{dpi−1,h+(j−h)aj},0≤h<j
进行斜率优化:
方程中去掉 m i n min min,将与 h h h相关的当做变量,与整理方程
− d p i − 1 , h = j ⋅ a j − h ⋅ a j − d p i , j -dp_{i-1,h}=j\cdot a_j-h\cdot a_j-dp_{i,j} −dpi−1,h=j⋅aj−h⋅aj−dpi,j
d p i − 1 , h = a j ⋅ h + d p i , j − j ⋅ a j dp_{i-1,h}=a_j\cdot h+dp_{i,j}-j\cdot a_j dpi−1,h=aj⋅h+dpi,j−j⋅aj
设 y = d p i − 1 , h y=dp_{i-1,h} y=dpi−1,h, x = h x=h x=h, k = a j k=a_j k=aj, b = d p i , j − j ⋅ a j b=dp_{i,j}-j\cdot a_j b=dpi,j−j⋅aj
则该方程就是直线方程 y = k x + b y=kx+b y=kx+b,决策点为 ( h , d p i − 1 , h ) (h, dp_{i-1,h}) (h,dpi−1,h),看直线过哪个决策点时截距 b b b最小,就可以求出 d p i , j dp_{i,j} dpi,j。
斜率 a j a_j aj随着 j j j的增大而增大,因此可以进行队头出队,取队头即为最优决策点。
求出的 p p p个饲养员接 m m m只猫的最小代价 d p p , m dp_{p,m} dpp,m,再减去所有 a a a点的加和 s u m A sumA sumA,即为最终结果。
【题解代码】
解法1:斜率优化动规 一般解法 设a点的前缀和
#include <bits/stdc++.h>
using namespace std;
#define N 100005
#define INF 0x3f3f3f3f3f3f3f3f
typedef long long LL;
LL n, m, p, d[N], s[N], a[N], dp[105][N], sumA[N];
int q[N], l = 1, r = 0;
LL X(int h)
{return h;
}
LL Y(int i, int h)
{return dp[i-1][h]+sumA[h];
}
LL K(int j)
{return a[j];
}
bool cmp(LL a1, LL b1, LL a2, LL b2)//a1/b1 <= a2/b2
{return a1*b2 <= a2*b1;
}
int main()
{int t, h;cin >> n >> m >> p;for(int i = 2; i <= n; ++i){cin >> d[i];s[i] = s[i-1]+d[i];//s:d数组的前缀和 }for(int i = 1; i <= m; ++i){cin >> h >> t;a[i] = t-s[h]; }sort(a+1, a+1+m);for(int i = 1; i <= m; ++i)sumA[i] = sumA[i-1]+a[i];//sumA:a数组的前缀和 memset(dp, 0x3f, sizeof(dp));for(int i = 0; i <= p; ++i)dp[i][0] = 0;for(int i = 1; i <= p; ++i)//先求出i-1行再求第i行 {l = 1, r = 0;//清空单调队列 for(int j = 1; j <= m; ++j)//0<=h<j{while(l < r && cmp(Y(i, j-1)-Y(i, q[r]), X(j-1)-X(q[r]), Y(i, q[r])-Y(i, q[r-1]), X(q[r])-X(q[r-1])))r--;q[++r] = j-1;while(l < r && cmp(Y(i, q[l+1])-Y(i, q[l]), X(q[l+1])-X(q[l]), K(j), 1))l++;int h = q[l];dp[i][j] = dp[i-1][h]+(j-h)*a[j]+sumA[h]-sumA[j];}}cout << dp[p][m];return 0;
}
解法2:最后减 a a a点加和
#include <bits/stdc++.h>
using namespace std;
#define N 100005
typedef long long LL;
LL n, m, p, d[N], s[N], a[N], dp[105][N], sumA;
int q[N*100], l = 1, r = 0;
LL X(int h)
{return h;
}
LL Y(int i, int h)
{return dp[i-1][h];
}
LL K(int j)
{return a[j];
}
bool cmp(LL a1, LL b1, LL a2, LL b2)//a1/b1 <= a2/b2
{return a1*b2 <= a2*b1;
}
int main()
{int t, h;cin >> n >> m >> p;for(int i = 2; i <= n; ++i){cin >> d[i];s[i] = s[i-1]+d[i];//s:d数组的前缀和 }for(int i = 1; i <= m; ++i){cin >> h >> t;a[i] = t-s[h];sumA += a[i];}sort(a+1, a+1+m);memset(dp, 0x3f, sizeof(dp));for(int i = 0; i <= p; ++i)dp[i][0] = 0;for(int i = 1; i <= p; ++i)//先求出i-1行再求第i行 {l = 1, r = 0;//清空单调队列 for(int j = 1; j <= m; ++j)//0<=h<j{while(l < r && cmp(Y(i, j-1)-Y(i, q[r]), X(j-1)-X(q[r]), Y(i, q[r])-Y(i, q[r-1]), X(q[r])-X(q[r-1])))r--;q[++r] = j-1;while(l < r && cmp(Y(i, q[l+1])-Y(i, q[l]), X(q[l+1])-X(q[l]), K(j), 1))l++;dp[i][j] = dp[i-1][q[l]]+(j-q[l])*a[j];}}cout << dp[p][m]-sumA;return 0;
}