复杂一点的四边形不等式和邮局

发布时间 2023-12-16 09:38:00作者: HL_ZZP

四边形不等式不仅在一维的线性dp中可以使用,在二维dp中也是很不错的东西
这个二维dp不局限于区间dp,虽然四边形不等式优化石子合并是很经典的东西

但是这种四边形不等式我不打算推导,而是直接背结论,因为我觉得知道推导过程对我的作用不是很大而且麻烦

在区间dp问题中,这样的方程\(f[i][j]=\displaystyle\max_{i\leq k<j}(f[i][k]+f[k+1][j]+w(i,j))\)是很常见
而四边形不等式有如下定理,如果满足下列条件:

1.函数\(w\)满足四边形不等式
2.\(\forall a\leq b \leq c\leq d\),有\(w(a,d)\leq w(b,c)\)
那么\(f\)也满足四边形不等式

\(f\)满足四边形不等式的意义在于,可以推导出\(f\) 的决策数组d满足$ d[i][j-1]\leq d[i][j] \leq d[i+1][j]$,那我们只需要在这个范围之内寻找最优决策就可以了。这样就优化掉了一个寻找最优决策的循环

但是,这个情况只能被柿子合并这种这么基础且特殊的区间dp的方程满足吗?

答案是不止。
看这道题吧 洛谷原题链接

[IOI2000] 邮局

题目描述

高速公路旁边有一些村庄。高速公路表示为整数轴,每个村庄的位置用单个整数坐标标识。没有两个在同样地方的村庄。两个位置之间的距离是其整数坐标差的绝对值。

邮局将建在一些,但不一定是所有的村庄中。为了建立邮局,应选择他们建造的位置,使每个村庄与其最近的邮局之间的距离总和最小。

你要编写一个程序,已知村庄的位置和邮局的数量,计算每个村庄和最近的邮局之间所有距离的最小可能的总和。

输入格式

第一行包含两个整数:第一个是村庄 \(V\) 的数量,第二个是邮局的数量 \(P\)

第二行包含 \(V\) 个整数。这些整数是村庄的位置。

输出格式

第一行包含一个整数\(S\),它是每个村庄与其最近的邮局之间的所有距离的总和。

样例 #1

样例输入 #1

10 5 
1 2 3 6 7 9 11 22 44 50

样例输出 #1

9

提示

对于 \(40\%\) 的数据,\(V \leq 300\)

对于 \(100\%\) 的数据,\(1 \leq P \leq 300\)\(P \leq V \leq 3000\),$1 \leq $ 村庄位置 \(\leq 10000\)

状态一眼出\(f[i][j]\)表示第\(j\)个邮局在第\(i\)个村庄时,前\(i\)个村庄的最小距离和
转移方程 \(f[i][j]=\displaystyle\max_{1\leq k<i}(f[k][j-1]+val(k,i))\), 其中\(val(k,i)\)表示\(k\)处和\(i\)处各有一个邮局时,第\(k\)个村庄到第\(i\)个村庄距离和
这样的朴素转移时间复杂度\(O(PV^2)\) 其中\(val(i,j)\)\(O(n^2)\)的预处理解决

但是无法通过这题。

想要进一步优化,两个方向,一个是状态,一个是转移。
状态已经是非常简洁了,我想不到能够怎么优化。
那就考虑转移
普通一些的转移优化的限制就是要把函数写开,不要有一些由变量和常量共同决定且不能简单得写成它们相乘的部分出现
如果能够写开的同时比较简单,那就是单调队列和斜率优化

但是这题自然是无法满足的。\(val[i][j]\)不能的写为\(i\)\(j\)的简单的函数(内含相乘和下取整)
主要是下取整不能简单的化开,不然就是斜率优化了吧

那除了这种优化,那我能够用的只有四边形不等式了,不行就是假题(逃
考虑证明\(val\)函数满足四边形不等式

\[要证明val函数满足四边形不等式,只需要证明val(i+1,j+1)+val(i,j) \geq val(i+1,j)+val(i,j+1)\\ 即是证明val(i+1,j+1)-val(i+1,j) \geq val(i,j+1)-val(i,j)\\ 从定义来看,这个式子显然成立\\ 证毕awa \]

然后,1. 显然\(val(a,d)\geq val(b,c)\),所以\(f\)满足四边形不等式
那么,根据那些题解里面说的,2.就有\(d[i][j-1]\leq d[i][j] \leq d[i+1][j]\)

但是!
上面这两步,真的成立吗?
这是没有证明的。因为这题的状态和转移和柿子合并是完全不一样的,连dp的类型都不同。
直接不加说明的套用,是完全没有逻辑的

我们应该是需要证明,当\(val\)满足四边形不等式且\(val(a,d)\geq val(b,c)\)时,状态和转移完全和柿子合并不同的这个dp,也是满足四边形不等式,而且,在这个dp满足四边形不等式的时候,这样子的转移方程和状态真的能够满足\(d[i][j-1]\leq d[i][j] \leq d[i+1][j]\)吗?

这是两个证明,应该都要写的。
同时,应该还要探究一下,什么样子的状态和方程能够满足这个性质,或者是,什么样子的状态和方程才不能够满足这个性质?

这样我觉得才算是理解了吧。。否则我觉得就是在不懂装懂。。之后遇到这样的题目就无脑套,然后连正确性都不知道。。
(那不就是不会。。)

下面是对命题1的证明。也就是证明,在val满足四边形不等式的时候,\(f\)也满足四边形不等式
也就是证明\(f[i+1][j+1]+f[i][j]\leq f[i+1][j]+f[i][j+1]\)

首先明确一下\(f[i][j]\)的定义,表示第\(j\)个邮局在第\(i\)个村庄时,前\(i\)个村庄的最小距离和。
\(f[i][j]=min(f[k][j-1]+val(k,i))\)
\(j=1\)时的情况

\[f[i+1][2]+f[i][1]\leq f[i+1][1]+f[i][2]\\ f[i+1][2]-f[i][2]\leq f[i+1][1]-f[i][1]\\ 设f[i][2]的最优决策为p,也就是f[i][2]=f[p][1]+val(p,i)\\ 而f[i+1][2]的最优决策可能不为p,也就是f[i+1][2]\leq f[p][1]+val(p,i+1)\\ 那上式就变为f[p][1]+val(p,i+1)-f[p][1]-val(p,i)\leq f[i+1][1]-f[i][1]\\ 那上式就变为val(p,i+1)-val(p,i)\leq (x[i+1]-x[i])*i\\ 后面这个很明显是更大的,这个只要理解了定义就能够感觉到吧\\ 所以得证了,f[i+1][2]+f[i][1]\leq f[i+1][1]+f[i][2]成立\\ 根据数学归纳法,那后面的一串应该也是成立的?回去查查蓝书,看看是怎么证明的\\ 也就是四边形不等式成立 \]

下面是决策单调性的证明

\[设p[i][j]为转移到f[i][j]的最优决策, 简记p=p[i][j]\\ 因为f[i][j]满足四边形不等式,所以对\forall k<p\\ f[p][j-1]+f[k][j]\geq f[p][j]+f[k][j-1]\\ f[k][j]-f[k][j-1]\geq f[p][j]-f[p][j-1]\\ 因为p是最优决策,所以\\ f[p][j-1]+val(p,i)\leq f[k][j-1]+val(k,i)\\ 然后上面两个式子加一下\\ f[k][j]+val(k,i)\geq f[p][j]+val(p,j)\\ 也就是p的决策一定比k的决策优秀 \\ 也就是p[i][j]\geq p[i-1][j] \]

很多不严谨的地方。。
我要再看看,然后我好像有点感觉了,这里面没有一个地方提到了k 的范围,除了决策点的地方
也就是说,k的范围大概率对结论是没有什么影响的,这很可能是一个普适的结论。

四边形不等式好像结论不是很复杂。。也就是我有地方疏漏了,导致我觉得应该有很多限制。。
仔细看看证明吧。

我在纸上又证明了一遍,这一列的dp方程的单调性是没有什么问题了。。
我现在基本掌握了怎么推导四边形不等式的结论了,不是很难。。
但是真的找不到普适性,只能对每一种的dp都尝试然后总结。
没办法,就这样吧

把这题的代码写了吧

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
	char c=getchar();int a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
int V,P;
int X[3001],f[3001][301],p[3001][301],w[3001][3001],SumX[3001];
//int bf(int x)//µÚÒ»¸öСÓÚxµÄÊý×ÖµÄϱí 
//{
//	int l=1,r=n;
//	while(l<r)
//	{
//		int mid=(l+r+1)>>1;
//		if(x>X[mid])l=mid;
//		else r=mid-1;
//	}
//	return l;
//}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	V=read(),P=read();
	for(int i=1;i<=V;i++)
	{
		X[i]=read();
	}
	sort(X+1,X+1+V);
	for(int i=1;i<=V;i++)
	{
		w[i][i]=0;
		for(int j=i+1;j<=V;j++)
		{
			w[i][j]=w[i][j-1]+X[j]-X[i+j>>1];
		}
	}
	memset(f,0x3f,sizeof(f));
	f[0][0]=0;
	for(int j=1;j<=P;j++)
	{
		p[V+1][j]=V;
		for(int i=V;i>=1;i--)
		{
			int minn=0x3f3f3f3f,minid;
			for(int k=p[i][j-1];k<=p[i+1][j];k++)
			{
				if(f[k][j-1]+w[k+1][i]<minn)
				{
					minn=f[k][j-1]+w[k+1][i];
					minid=k;
				}
			}
			f[i][j]=minn;
			p[i][j]=minid;
		}
	}
	cout<<f[V][P]<<endl;
	return 0;
}

唉唉
我这种状态还是不好
统计代价需要二分,而且式子很复杂
现在这个代码的状态是\(f[i][j]表示前i个村子有j个邮局\),和我之前的状态的区别在于,不要求第j个邮局在第i个村子
这样会导致答案偏大,因为你不知道你的村子到底是距离上一个邮局进还是距离这个邮局近,而是直接默认离这个邮局近
但是无所谓,因为这样只会让答案更劣,而真正的正确答案我们能够保证可以被找到,所以这个更劣的答案肯定会被覆盖。。
看了,我还是太年轻了。。
写转移方程的时候一定要想想,代价好统计吗,这可是转移方程两个复杂度的部分中的一个啊。。
不过这个思路我已经遇到很多次了,倒是很好理解,但是要在自己写的时候想到呢,还得多练
注意一下就好了

额,但是对我自己的那个转移方程的四边形不等式证明的正确性我倒是能保证。。
哎哎,我的转移还有一个问题,就是只有一个邮局和最后一个邮局在我这里是特殊情况,最后一个邮局好说,遍历找答案就行
第一个邮局的特殊会导致我根本就找不到第一个转移点,从而我后面的决策单调性优化都出问题
唉唉。所以还是换了各种题解都用的状态和写法。。
也难怪大家都用啊