【模板】决策单调性优化DP

又是优化DP,孩子人都傻了。

什么是决策单调性

如果有dpi=minj < i(dpj+δj,i)

并保证对于∀i,j(i<j),有∃k,使得∀pos∈[0,k],dpi+δi,pos≤dpj+δj,pos且∀pos∈(k,n],dpi+δi,pos>dpj+δj,pos,则说明决策有单调性。

(不就是说某个位置之前的点用i转移更优,而之后的点用j转移更优嘛)

如何判断是否满足决策单调性

1.满足四边形不等式的一定满足决策单调性

想起四边形不等式想起四边形不等式想起⌈四边形不等式⌋:∀p1≤p2≤p3≤p4,有δp1,p3+δp2,p4≤δp1,p4+δp2,p3

然后发现四边形不等式决策单调性四边形不等式决策单调性四边形不等式⇒决策单调性,通了反证法。

若若若i<j<k1<k2

且且且t(i,k1)>t(j,k1)

t(i,k2)≤t(j,k2)

我们知道我们知道我们知道

w(i,k1)+w(j,k2)≤w(i,k2)+w(j,k1)

所以可以得到所以可以得到所以可以得到

t(i,k2)+w(i,k1)+w(j,k2)≤w(i,k2)+w(j,k1)+t(j,k2)

所以有所以有所以有

t(i,k1)≤t(j,k1)

与题设相悖与题设相悖与题设相悖

优化吧

1.一层分治

征途征途P4072 [SDOI2016]征途

dpdate,i=minj=1i−1(dpdate−1,j+(prei−prej)2)

#include <stdio.h>
#include <string.h>
const int N = 4096;
int n, m;
int a[N];
int dp[N][N];
int date;
int delta(int f,int t) {
	return dp[date-1][f]+(a[f]-a[t])*(a[f]-a[t]);
	//每一天的状态只与上一天有关;
	//其实可以滚动压数组大小;
}
void devide(int l,int r,int L,int R) {
	//l~r:目前要求解的区间;
	//L~R:解可能产生的区间;
	if(l > r) 
		return;
	int mid = (l+r)>>1;
	int pos = L, res = 998244353;
	for(int i = L;i <= R;++i) {
		int del = delta(i,mid);
		if(del < res) {
			res = del;
			pos = i;
		}
	}//枚举解的区间;
	dp[date][mid] = res;
	devide(l,mid-1,L,pos);
	devide(mid+1,r,pos,R);
	//因为一个地点可能负责产生很多数的最优解;
	//所以pos不加不减;
}
signed main() {
	scanf("%d %d",&n,&m);
	for(int i = 1, temp;i <= n;++i) {
		scanf("%d",&temp);
		a[i] = a[i-1]+temp;
	}
	memset(dp,0x3f,sizeof(dp));
	dp[0][0] = 0;
	for(int i = 1;i <= m;++i) {
		date++;
		devide(1,n,0,n-1);
	}
	printf("%lld",1ll*dp[m][n]*m-1ll*a[n]*a[n]);
}

2.二层分治

玩具装箱玩具装箱P3195[HNOI2008]玩具装箱

用到了两层分治的原因是,这个的状态与之前的状态有关,必须先把之前的状态算出来。

dpi=minj=1i−1(dpj+(i−(j+1)+prei−prej−L)2)

#include <stdio.h>
#include <string.h>
#include <bits/stl_algobase.h>
using namespace std;
const int N = 5e4+10;
int n, L;
long long a[N];
long long dp[N];
long long delta(int f,int t) {
	return dp[f]+(t-f-1+a[t]-a[f]-L)*(t-f-1+a[t]-a[f]-L);
}
void devide(int l,int r,int L,int R) {
	if(l > r) 
		return;
	int mid = (l+r)>>1;
	int pos = L;
	long long res = 99999999998244353;
	//开的要大;
	for(int i = L;i <= R;++i) {
		long long del = delta(i,mid);
		if(del < res) {
			res = del;
			pos = i;
		}
	}
	dp[mid] = min(dp[mid],res);
	devide(l,mid-1,L,pos);
	devide(mid+1,r,pos,R);
	//几乎一模一样;
}
void solve(int l,int r) {
	if(l == r) 
		return;
	int mid = (l+r)>>1;
	solve(l,mid);
	//先处理前面的;
	devide(mid+1,r,l,mid);
	solve(mid+1,r);
}
signed main() {
	scanf("%d %d",&n,&L);
	for(int i = 1, temp;i <= n;++i) {
		scanf("%d",&temp);
		a[i] = a[i-1]+temp;
	}
	memset(dp,0x3f,(n+1)*sizeof(long long));
	dp[0] = 0;
	solve(0,n);
	printf("%lld",dp[n]);
}

3.二分栈

#include <stdio.h>
#include <bits/stl_algobase.h>
using namespace std;
const int N = 5e4+10;
long long a[N];
int n, L;
long long dp[N];
long long delta(int f,int t) {
	return dp[f]+(t-f-1+a[t]-a[f]-L)*(t-f-1+a[t]-a[f]-L);
}
struct NODE {
	int x;
	int l, r;
	//x:位置;
	//l~r:x负责的区间;
	NODE () {x = l = r = 0;}
	NODE (int _in_a,int _in_b,int _in_c) {
		x = _in_a;
		l = _in_b, r = _in_c;
	}
} que[N];
int head = 1, tail;
signed main() {
	scanf("%d %d\n",&n,&L);
	for(int i = 1, temp;i <= n;++i) {
		scanf("%d",&temp);
		a[i] = a[i-1]+temp;
	}
	que[++tail] = NODE(0,1,n);
	for(int i = 1;i <= n;++i) {
		while(que[head].r < i) 
			head++;
		dp[i] = delta(que[head].x,i);
		while(head <= tail&&delta(que[tail].x,que[tail].l) >= delta(i,que[tail].l)) 
			tail--;
		if(head > tail) {
			que[++tail] = NODE(i,i+1,n);
			continue;
		}
		int l = max(que[tail].l,i+1), r = que[tail].r;
		while(l <= r) {
			int mid = (l+r)>>1;
			if(delta(que[tail].x,mid) >= delta(i,mid)) 
				r = mid-1;
			else 
				l = mid+1;
		}
		que[tail].r = r;
		if(l <= n) 
			que[++tail] = NODE(i,l,n);
	}
	printf("%lld",dp[n]);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野生的狒狒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值