• poj 1821 Fence(单调队列优化DP)


    poj 1821 Fence




    $ solution: $

    这道题因为每一个粉刷的人都有一块“必刷的木板”,所以可以预见我们的最终方案里的粉刷匠一定是按其必刷的木板的顺序排列的。这就提示了我们可以用线性 $ DP $ ,只需要将粉刷匠按必刷的木板排序即可。

    设 $ F[ $ i $ ][j] $ 表示前 $ i $ 个粉刷匠刷了前 $ j $ 快木板的最大收益(可以有木板不刷!)。我们可以根据题意列出转移方程:

    1. 首先这个粉刷匠一块木板也不刷
    2. 这块木板不刷
    3. 这个粉刷匠从第k块木板刷到第 $ j $ 块木板(这个有限制)

    于是我们列出方程:

    $ F[i][j]=max{F[i-1][j],quad F[i][j-1],quad ^{quad max}_{j-L_ileq k< S_i}{F[i-1][k]+P_i imes (j-k) } } $

    然后我们发现前两个都好转移,但最后一个需要枚举耗费大量时间,而我们的数据范围就很不友好了。于是我们考虑如何优化:首先先将与k无关的项提取出来,这个可以直接放外面。

    $ F[i][j]=^{quad max}_{j-L_ileq k< S_i}{F[i-1][k]-P_i imes k }+P_i imes j $

    然后我们发现现在 $ max $ 函数里面的东西基本只和k有关, $ i $ 在循环枚举的外围可以相当于定值,于是我们可以用一个单调队列维护 $ F[i-1][k]-P_i imes k $ 这个东西,我们不能直接更新最大值,因为 $ j-L_ileq k< S_i $ 会使我们之前的最大值失效,所以我们的单调队列需要满足双向删除,取队头(对头就是最优决策)。

    总复杂度 $ O(N imes M) $



    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<map>
    #include<set>
    
    #define ll long long
    #define db double
    #define inf 0x7fffffff
    #define rg register int
    
    using namespace std;
    
    int n,m;
    int b[16005];
    int q[16005];
    int f[105][16005];
    
    struct su{
    	int l,s,v;
    	inline bool operator <(su x){
    		return s<x.s;
    	}
    }a[105];
    
    inline int ff(int i,int j){
    	return f[i-1][j]-a[i].v*j;
    }
    
    inline int qr(){
    	register char ch; register bool sign=0; rg res=0;
    	while(!isdigit(ch=getchar())) if(ch=='-')sign=1;
    	while(isdigit(ch)) res=res*10+(ch^48),ch=getchar();
    	return sign?-res:res;
    }
    
    int main(){
    	//freopen(".in","r",stdin);
    	//freopen(".out","w",stdout);
    	m=qr(); n=qr();
    	for(rg i=1;i<=n;++i){
    		a[i].l=qr(); a[i].v=qr(); a[i].s=qr();
    	} sort(a+1,a+n+1);
    	for(rg i=1;i<=n;++i){ rg l=1,r=0;
    		for(rg j=max(0,a[i].s-a[i].l);j<a[i].s;++j){
    			while(l<=r&&ff(i,j)>=ff(i,q[r]))--r;; q[++r]=j;
    		}
    		for(rg j=1;j<=m;++j){
    			f[i][j]=max(f[i-1][j],f[i][j-1]);
    			if(j>=a[i].s){
    				while(l<=r&&q[l]<j-a[i].l)++l;
    				if(l<=r)f[i][j]=max(f[i][j],ff(i,q[l])+a[i].v*j);
    			}
    		}
    	}printf("%d
    ",f[n][m]);
    	return 0;
    }
    
  • 相关阅读:
    java 模块调用
    JAVA 方法引用
    JAVA 接口中静态方法
    JAVA 反射获取class类对象
    JAVA 引用对象的实例方法
    JAVA Lambda表达式的使用
    JAVA 接口中默认方法
    JAVA中Function的使用
    JAVA TCP客户端读区文件,服务端写入文件
    JAVA 反射练习
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/11002723.html
Copyright © 2020-2023  润新知