关于决策单调性
四边形不等式与决策单调性
有如下的定理:
如果对于 ,有 成立,则此函数满足四边形不等式。
对于方程:
定义使 取到最优值的 称为 决策点,记作 。
若 满足四边形不等式则 。
这样的话,在求 时只需从 循环到
枚举长度 ,总时间为:
故总时间复杂度为
[IOI2013]wombats
对于决策单调性,可以用四边形不等式证明,实际中更常用的方法是感性理解以及打表找规律
本题中,可以发现最优路径不会交叉,因此利用上面的方式将 从 优化到
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int R,C,E;
int h[5005][205],v[5005][205],Loc[205][205];
struct mat{
int a[205][205];
inline void init(int _x=1e9){
for(int i=0;i<=R+1;i++){
for(int j=0;j<=R+1;j++)a[i][j]=1e9;
a[i][i]=_x;
}
}
inline int* operator [](int t){
return a[t];
}
inline mat operator *(mat b){
mat res;res.init();
for(int i=0;i<=R+1;i++){
for(int j=0;j<=R+1;j++)Loc[i][j]=0;
}
for(int i=1;i<=R;i++){
for(int j=R;j>=1;j--){
int l=(1,Loc[i-1][j]?Loc[i-1][j]:1),r=(Loc[i][j+1]?Loc[i][j+1]:R);
for(int t=l;t<=r;t++){
if(res[i][j]>a[i][t]+b[t][j]){
res[i][j]=a[i][t]+b[t][j];
Loc[i][j]=t;
}
}
}
}return res;
}
}tree[805];
inline void calc(int l,int r,mat &mp){
mp.init(0);
for(int i=l;i<=r;i++){
for(int j=1;j<=R;j++){
for(int t=1;t<=R;t++)mp[j][t]+=v[i][t];
}
for(int j=1;j<=R;j++){
for(int t=1;t<=R;t++)mp[j][t]=min(mp[j][t],mp[j][t-1]+h[i][t-1]);
for(int t=R;t>=1;t--)mp[j][t]=min(mp[j][t],mp[j][t+1]+h[i][t]);
}
}
}
void build(int l=1,int r=C,int i=1){
if(r-l<25){
calc(l,r,tree[i]);return ;
}
int mid=(l+r)>>1;
build(l,mid,i<<1);build(mid+1,r,i<<1|1);
tree[i]=tree[i<<1]*tree[i<<1|1];
}
void update(int loc,int l=1,int r=C,int i=1){
if(loc<l||loc>r)return ;
if(r-l<25){
calc(l,r,tree[i]);return ;
}
int mid=(l+r)>>1;
update(loc,l,mid,i<<1);update(loc,mid+1,r,i<<1|1);
tree[i]=tree[i<<1]*tree[i<<1|1];
}
int main(){
scanf("%d%d",&C,&R);
for(int i=1;i<=C;i++){
for(int j=1;j<R;j++)scanf("%d",&h[i][j]);
}
for(int i=2;i<=C;i++){
for(int j=1;j<=R;j++)scanf("%d",&v[i][j]);
}
build();
scanf("%d",&E);
while(E--){
int op,x,y,z;
scanf("%d",&op);
if(op==1){
scanf("%d%d%d",&x,&y,&z);x++;y++;
h[x][y]=z;update(x);
}
else if(op==2){
scanf("%d%d%d",&x,&y,&z);x+=2;y++;
v[x][y]=z;update(x);
}
else if(op==3){
scanf("%d%d",&x,&y);x++;y++;
printf("%d\n",tree[1][x][y]);
}
}
return 0;
}
[IOI2000]邮局
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int v,p;
int dp[3005][305],w[3005][3005];
int a[3005],loc[3005][305];
inline void init(){
for(int i=1;i<=v;i++){
for(int j=i;j<=v;j++)w[i][j]=w[i][j-1]+a[j]-a[(i+j)>>1];
}
}
int main(){
scanf("%d%d",&v,&p);
for(int i=1;i<=v;i++)scanf("%d",&a[i]);
sort(a+1,a+v+1);init();
memset(dp,0x3f,sizeof(dp));dp[0][0]=0;
for(int i=1;i<=v;i++){
for(int j=p;j;j--){
int l=(loc[i-1][j]?loc[i-1][j]:1),r=(loc[i][j+1]?loc[i][j+1]:i);
for(int t=l;t<=r;t++){
if(dp[i][j]>dp[t-1][j-1]+w[t][i]){
dp[i][j]=dp[t-1][j-1]+w[t][i];
loc[i][j]=t;
}
}
}
}printf("%d",dp[v][p]);
return 0;
}
分治优化
设 表示要求的区间为 ,决策区间为 ,每次选 中点 暴力求解其决策点
然后递归 和 。
递归 层,每层暴力求 的复杂度之和为 ,故总复杂度为
CF321E Ciel and Gondolas
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,k;
void Read( int &x ) {
x = 0; int f = 1;
char s = getchar( );
for( ; s < '0' || s > '9' ; s = getchar( ) ) f = s == '-' ? -f : f;
for( ; s >= '0' && s <= '9' ; s = getchar( ) ) x = x * 10 + s - '0';
x *= f;
}
int a[4005][4005];
long long sum[4005][4005],dp[805][4005];
void solve(int lim,int l=1,int r=n,int ql=0,int qr=n){
if(l>r)return ;
int mid=(l+r)>>1,loc=0;
for(int i=ql;i<=mid&&i<=qr;i++){
if(dp[lim][mid]>dp[lim-1][i]+sum[i+1][mid]){
dp[lim][mid]=dp[lim-1][i]+sum[i+1][mid];
loc=i;
}
}
solve(lim,l,mid-1,ql,loc);solve(lim,mid+1,r,loc,qr);
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)Read(a[i][j]);
}
for(int i=1;i<=n;i++)sum[i][i]=a[i][i];
for(int len=1;len<=n;len++){
for(int i=1;i+len<=n;i++){
int j=i+len;
sum[i][j]=sum[i][j-1]+sum[i+1][j]+a[i][j]+a[j][i]-sum[i+1][j-1];
}
}
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=k;i++)solve(i);
printf("%lld",dp[k][n]/2);
return 0;
}
CF868F Yet Another Minimization Problem
点击查看代码
```cpp #includereturn 0;
}
</details>
### [[IOI2014]holiday 假期](https://www.luogu.com.cn/problem/P5892 "[IOI2014]holiday 假期")
<details>
<summary>点击查看代码</summary>
```cpp
#include<bits/stdc++.h>
using namespace std;
int n,s,d;
int a[100005];
int rt[100005],tree[4000005],le[4000005],ri[4000005],cnt;
long long sum[4000005];
void insert(int old,int &i,int loc,int l=0,int r=1e9+1){
if(loc<l||loc>r)return ;
i=++cnt;sum[i]=sum[old]+loc;le[i]=le[old];ri[i]=ri[old];tree[i]=tree[old]+1;
if(l==r)return ;
int mid=(1ll*l+r)>>1;
insert(le[old],le[i],loc,l,mid);insert(ri[old],ri[i],loc,mid+1,r);
}
long long query(int a,int b,int k,int l=0,int r=1e9+1){
if(k<=0)return 0;
if(k>=tree[a]-tree[b])return sum[a]-sum[b];
if(l==r)return 1ll*k*l;
int mid=(1ll*l+r)>>1;
if(tree[ri[a]]-tree[ri[b]]>=k)return query(ri[a],ri[b],k,mid+1,r);
return query(le[a],le[b],k-tree[ri[a]]+tree[ri[b]],l,mid)+sum[ri[a]]-sum[ri[b]];
}
inline long long calc1(int l,int r){
int k=d-r+l-r+s;
return query(rt[r],rt[l-1],k);
}
inline long long calc2(int l,int r){
int k=d-r+l-s+l;
return query(rt[r],rt[l-1],k);
}
long long ans;
void solve(long long calc(int,int),int l=1,int r=s,int ql=s,int qr=n){
if(l>r)return ;
int mid=(l+r)>>1,loc=ql;
long long res=calc(mid,ql);
for(int i=ql;i<=qr;i++){
long long tmp=calc(mid,i);ans=max(ans,tmp);
if(tmp>res){
res=tmp;loc=i;
}
}
solve(calc,l,mid-1,ql,loc);solve(calc,mid+1,r,loc,qr);
}
int main(){
scanf("%d%d%d",&n,&s,&d);s++;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
insert(rt[i-1],rt[i],a[i]);
}
solve(calc1);solve(calc2);
printf("%lld",ans);
return 0;
}
单调栈优化
在单调栈中保存三元组 表示 这些位置的决策是 ,初始时保存 ,每处理一个位置,这个位置就可以作为决策。
计算单调栈中哪些决策应修改为当前位置,这些位置一定是一个后缀,如果某一个三元组左端点还不如新的决策优,那么整个三元组都可以删去。然后再在某个三元组上二分,得到决策区间,将这个三元组留下前半部分并加入这次的位置的新三元组。
[NOI2009] 诗人小G
点击查看代码
#include<bits/stdc++.h>
using namespace std;
long long t,n,l,p;
long long sum[100005];
long double dp[100005];
int ql[100005],qr[109005],top,ed;
int pre[100005];
int stk[100005],tp;
char s[100005][33];
inline long double ksm(long double a,long long b){
long double res=1;
while(b){
if(b&1)res*=a;
a*=a;b>>=1;
}
return res;
}
inline long double calc(int i,int j){
return dp[j]+ksm(abs(sum[i]-sum[j]-1-l),p);
}
inline int bound(int i,int j,int x){
int l=x,r=n+1;
while(l<=r){
int mid=(l+r)>>1;
if(calc(mid,i)<=calc(mid,j))r=mid-1;
else l=mid+1;
}
return l;
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld%lld%lld",&n,&l,&p);
for(int i=1;i<=n;i++){
scanf("%s",s[i]);
sum[i]=sum[i-1]+strlen(s[i])+1;
}
tp=top=ed=0;qr[top]=n;
for(int i=1;i<=n;i++){
while(top<ed&&qr[top]<=i)top++;
dp[i]=calc(i,ql[top]);pre[i]=ql[top];
while(top<ed&&qr[ed-1]>=bound(i,ql[ed],qr[ed-2]+1))ed--;
qr[ed]=bound(i,ql[ed],qr[ed-1]+1);ql[++ed]=i;
}
if(dp[n]>1e18){
puts("Too hard to arrange");
puts("--------------------");
continue;
}
printf("%.0Lf\n",dp[n]);
for(int i=stk[tp]=n;i;stk[++tp]=i=pre[i]);
for(int i=tp;i;i--){
for(int j=stk[i]+1;j<=stk[i-1];j++){
if(j!=stk[i-1])printf("%s ",s[j]);
else printf("%s\n",s[j]);
}
}
puts("--------------------");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!