• 决策单调性优化DP+分治优化决策单调性


    前言

    本来这篇已经写了(frac{2}{3})了 然后我关机时忘保存了。。。


    华丽的分割线

    决策单调性优化(DP)

    对于类似于

    [dp[i][j]=max/min(dp[k - 1][j - 1] + count(k,i)) ]

    不妨设 当 最后一次 (max/min)更新时

    [f(i,j)=k ]

    若有

    [forall i,jin[1,n],s.t. i < j Rightarrow f(i,k)<=f(j,k) ]

    我就可以称她具有决策单调性

    例题

    [HNOI2008]玩具装箱TOY

    题意我就不概括了

    据题 容易推出状态方程

    [dp[i]=min(dp[j-1]+count(i,j)) ]

    凭感觉 是具有决策单调性的

    其实可以证明

    不过我太菜了 不会

    既然决策具有单调性

    那么对于每一个决策点 我们可以拿出一个决策区间

    用一个双端队列维护 决策点 和 决策区间

    在每一次循环前

    把区间(.r<i)的舍去

    然后再以当前点为决策点看是否能比队列中的对后面的贡献更小

    while(l <= r&&dp[i] + count(q[r].l,i) <= dp[q[r].pos] + count(q[r].l,q[r].pos)) r--;
    

    注意(while)结束后还要特判一下

    [dp[i] + count(q[r].l,i) <= dp[q[r].pos] + count(q[r].l,q[r].pos) ]

    不见得

    (forall x in[q[r].l,q[r].r]Rightarrow dp[i] + count(x,i) > dp[q[r].pos] + count(x,q[r].pos))

    所以还要二分找一下那个特殊的位置

    因为决策点具有单调性(Rightarrow)决策区间具有单调性(Rightarrow)维护的双端队列具有单调性

    与单调队列类似 每点只进入和删除一次

    但是有二分

    时间复杂度(O(nlogn))

    (Code)

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= x&&x <= '9')
    template<typename T>
    inline T Read(T Type)
    {
    	T x = 0,f = 1;
    	char a = getchar();
    	while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
    	while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
    	return x * f;
    }
    typedef long long ll;
    const int MAXN = 50010;
    int n,L,a[MAXN];
    ll dp[MAXN],sum[MAXN];
    struct node
    {
    	int pos,l,r;
    	void ass(int Pos,int L,int R) {pos = Pos,l = L,r = R;}
    }q[MAXN];
    inline ll co(ll x) {return x * x;}
    inline ll count(int i,int j) {return co(sum[i] - sum[j] + i - j - 1 - L);}
    inline int get_(int x,node seq)
    {
    	int l = seq.l,r = seq.r;
    	while(l <= r)
    	{
    		int mid = l + r >> 1;
    		if(dp[x] + count(mid,x) <= dp[seq.pos] + count(mid,seq.pos))
    		{
    			if(r == mid) return r;
    			r = mid - 1;
    		}
    		else l = mid + 1;
    	}
    	return l;
    }
    int main()
    {	n = Read(1),L = Read(1);
    	for(reg i = 1;i <= n;i++) sum[i] = (a[i] = Read(1)) + sum[i - 1];
    	memset(dp,0x7f7f7f,sizeof(dp));
    	dp[0] = 0;
    	int l = 1,r = 0;
    	q[++r].ass(0,1,n);
    	for(reg i = 1;i <= n;i++)
    	{
    		while(q[l].r < i) l++;
    		dp[i] = dp[q[l].pos] + count(i,q[l].pos);
    		q[l].l = i + 1;
    		while(l <= r&&dp[i] + count(q[r].l,i) <= dp[q[r].pos] + count(q[r].l,q[r].pos)) r--;
    		int pos = get_(i,q[r]);
    		q[r].r = pos - 1;
    		if(pos <= n) q[++r].ass(i,pos,n);
    	}
    	printf("%lld
    ",dp[n]);
    	return 0;
    }
    

    这类(DP) 通常还能再优化

    也许是用斜率 或单调队列

    但是决策单调性的好想,好实现及其优于暴力的特点让我们常常使用

    分治优化决策单调性

    因为 决策具有单调性

    那么就可以使用分治优化

    CF321E Ciel and Gondolas

    #include <cstdio>
    #include <string>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define reg register int
    #define isdigit(x) ('0' <= x&&x <= '9')
    template<typename T>
    inline T Read(T Type)
    {
        T x = 0,f = 1;
        char a = getchar();
        while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
        while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
        return x * f;
    }
    const int MAXN = 4010,inf = 1000000000;
    int sum[MAXN][MAXN],dp[MAXN][810];
    inline int count(int i,int j) 
    {return sum[i][i] - sum[i][j - 1] - sum[j - 1][i] + sum[j - 1][j - 1];}
    inline void dfs(int k,int l,int r,int opl,int opr)
    {
        if(l > r) return;
        int mid = l + r >> 1;
    	int minl = inf,id;
        for(int i = opl;i <= min(opr,mid);i++)
        {
        	int cur = dp[i - 1][k - 1] + count(mid,i);
    		if(cur < minl) minl = cur,id = i;
    	}
        dp[mid][k] = minl;
        dfs(k,l,mid - 1,opl,id);
        dfs(k,mid + 1,r,id,opr);
    }
    int main()
    {
        int n = Read(1),k = Read(1);
        for(reg i = 1;i <= n;i++)
            for(reg j = 1;j <= n;j++)
            {
            	 int v = Read(1);
    			 sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + v;
    		}
        for(reg i = 1;i <= n;i++) dp[i][0] = inf;
        for(reg i = 1;i <= k;i++) dfs(i,1,n,1,n);
        printf("%d",dp[n][k] / 2);
        return 0;   
    } 
    
  • 相关阅读:
    pycharm 2016.2注册码
    python selenium2
    webdriver.py--解说
    Sahi ---实现 Web 自动化测试
    性能测试
    看云-git类的书籍写作
    IntelliJ IDEA 对于generated source的处理
    各种常用的序列化性能的对比
    rpc框架--grpc-java
    grpc mvn protobuf:compile 过程
  • 原文地址:https://www.cnblogs.com/resftlmuttmotw/p/12002741.html
Copyright © 2020-2023  润新知