【算法入门】单调队列优化动态规划:[RomaniaOI2002]Fence

Problem

Description
A team of k (1 <= K <= 100) workers should paint a fence which contains N (1 <= N <= 16 000) planks numbered from 1 to N from left to right. Each worker i (1 <= i <= K) should sit in front of the plank Si and he may paint only a compact interval (this means that the planks from the interval should be consecutive). This interval should contain the Si plank. Also a worker should not paint more than Li planks and for each painted plank he should receive Pi $ (1 <= Pi <= 10 000). A plank should be painted by no more than one worker. All the numbers Si should be distinct.
Being the team’s leader you want to determine for each worker the interval that he should paint, knowing that the total income should be maximal. The total income represents the sum of the workers personal income.
Write a program that determines the total maximal income obtained by the K workers.
Input
The input contains:
Input
N K
L1 P1 S1
L2 P2 S2

LK PK SK
Semnification
N -the number of the planks; K ? the number of the workers
Li -the maximal number of planks that can be painted by worker i
Pi -the sum received by worker i for a painted plank
Si -the plank in front of which sits the worker i
Output
The output contains a single integer, the total maximal income.
Sample Input
8 4
3 2 2
3 2 3
3 3 5
1 1 7
Sample Output
17


Solution

  • 拿到这道题以后,对si排序是一定的。这样才能保证算法的有序性。
  • 对于这道题,考虑最简单的DP,设 f [ i ] [ j ] f[i][j] f[i][j]为前i个木匠,粉刷到第j块木板的最大收益。
  • ① ① 当第 i i i个木匠一块也不刷时: f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j]
    ② ② 当第 i i i个木匠不粉刷第j块时: f [ i ] [ j ] = f [ i ] [ j − 1 ] f[i][j]=f[i][j-1] f[i][j]=f[i][j1]
    ③ ③ i i i块木匠粉刷第 j j j块时,从第 k + 1 k+1 k+1块粉刷到 j j j
    此时要满足位置关系: k + 1 ≤ S i ≤ j k+1≤Si≤j k+1Sij;也要满足长度关系: j − k ≤ L j-k≤L jkL
    则方程: f [ i ] [ j ] = m i n ( f [ i − 1 ] [ k ] + ( j − k ) ∗ p i ) ( j − L i ≤ k ≤ S i − 1 , j ≥ s i ) f[i][j]=min(f[i-1][k]+(j-k)*pi)(j-Li≤k≤Si-1,j≥si) f[i][j]=min(f[i1][k]+(jk)pi)(jLikSi1,jsi
  • 时间复杂度: O ( N 2 K ) O(N^2K) O(N2K)
  • 我们考虑对情况 ③ ③ 进行优化
  • f [ i ] [ j ] = m i n ( f [ i − 1 ] [ k ] + ( j − k ) ∗ p i ) f[i][j]=min(f[i-1][k]+(j-k)*pi) f[i][j]=min(f[i1][k]+(jk)pi)
    → f [ i ] [ j ] = m i n ( f [ i − 1 ] [ k ] + j ∗ p i − k ∗ p i ) →f[i][j]=min(f[i-1][k]+j*pi-k*pi) f[i][j]=min(f[i1][k]+jpikpi)
    → f [ i ] [ j ] = j ∗ p i + m i n ( f [ i − 1 ] [ k ] − k ∗ p i ) ( 此 时 i , j 可 视 为 常 量 ) →f[i][j]=j*pi+min(f[i-1][k]-k*pi) (此时i,j可视为常量) f[i][j]=jpi+min(f[i1][k]kpi)(ij)
    此 时 满 足 ( j − L i ≤ k ≤ S i − 1 , j ≥ s i ) 此时满足(j-Li≤k≤Si-1,j≥si) (jLikSi1,jsi)
  • 对于每一个 k k k,我们发现其下界 j − L i j-Li jLi是随着j的增大而增大,而上界 S i − 1 Si-1 Si1是不变的。
    此时,每一个 k k k都对应着一个决策的值;并且,当当前的 j j j舍弃了某个 k k k以后,后面的 j j j同样用不到该决策 k k k。因此,我们可以用一个降序的单调队列来维护这一个决策的最优值。
  • 对于这道题具体的做法是:
    S t e p 1 Step1 Step1:将所有的决策塞到单调队列中并保持决策的单调性。
    S t e p 2 Step2 Step2:在枚举j时将不合法的队头删去。
    S t e p 3 Step3 Step3:在队列不为空时取队头进行状态转移。
  • 代码如下:
#include
#include
#include
#include
using namespace std;
struct node{int l,p,s;};
node a[200];
int N,K,t,h;
int L[200],P[200],S[200],f[200][50000],q[50000];
inline bool cmp(node a,node b)
{
	if (a.s<b.s) return true;
	return false;
}
#define val(i,k) (f[i-1][k]-k*P[i])
int main(void)
{
	cin>>N>>K;
	for (int i=1;i<=K;++i)
	    cin>>a[i].l>>a[i].p>>a[i].s;
	sort(a+1,a+K+1,cmp);
	//按si降序排序 
	for (int i=1;i<=K;++i)
	    L[i]=a[i].l,P[i]=a[i].p,S[i]=a[i].s;
	//方便调用 
	    
	for (int i=1;i<=K;++i)
	{
		for (int j=max(0,S[i]-L[i]);j<=S[i]-1;++j)
		{
			while (h<=t && val(i,j)>=val(i,q[t])) --t;
			q[++t]=j;
		}
		//将所有决策进队 
		
		for (int j=1;j<=N;++j)
		{
			f[i][j]=max(f[i-1][j],f[i][j-1]);
			//第1,2种情况的状态转移 
			
			if (j<S[i]) continue;
			
			while (h<=t && q[h]<j-L[i]) h++;
			//排除非法状态 
			if (h<=t) f[i][j]=max(f[i][j],f[i-1][q[h]]+(j-q[h])*P[i]);
			//第3种情况的状态转移 
		}
	}
	cout<<f[K][N]<<endl;
	return 0; 
} 

最后再对单调队列优化的情况进行一些总结:
状态转移方程形如: f [ i ] = m i n ( f [ j ] + v a l ( i , j ) , L ( i ) ≤ j ≤ R ( i ) f[i]=min(f[j]+val(i,j),L(i)≤j≤R(i) f[i]=min(f[j]+val(i,j),L(i)jR(i)
其中, j j j的上下界变化具有单调性;多项式 v a l ( i , j ) val(i,j) val(i,j)中的每一个单项式只能与 i i i j j j中的一项有关,不能同时包含 i i i j j j的乘积项。
这就是单调队列优化
本文若有疏漏或错误之处,欢迎读者指正。

你可能感兴趣的:(动态规划DP)