又是优化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.一层分治
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.二层分治
用到了两层分治的原因是,这个的状态与之前的状态有关,必须先把之前的状态算出来。
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]);