BZOJ4707 : B君的技巧

本文介绍了一种利用线段树进行动态规划优化的方法,通过减少状态空间来降低时间复杂度至O(n^3),同时进一步优化空间复杂度至O(n^2)。通过具体的代码实现展示了如何有效地进行区间划分及更新。

建立线段树,设$f[x][l][r]$表示当前考虑$x$点,最左端是$l$,最右端是$r$的最少代价。

如果$a$在$x<<1$,$d$在$x<<1|1$,

设$g[a][c]=\min(f[x<<1][a][b]+w[b][c])$,

则$f[x][a][d]=\min(g[a][c]+f[x<<1|1][c][d])$。

对于$a$在$x<<1|1$,$d$在$x<<1$的情况可以类似处理。

时间复杂度$O(n^3)$。

对于空间上的优化,因为对于同一深度的每个$x$来说,其有效的$l,r$的取值区间互不相交,所以可以省去$x$这一维。

空间复杂度$O(n^2)$。

 

#include<cstdio>
const int N=515,inf=~0U>>2;
int K,n,m,m2,i,j,l,r,mid,x,y,z,ans,w[N][N],f[N][N],g[N][N],h[N][N];
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
inline void up(int&a,int b){if(a>b)a=b;}
int main(){
  read(K);n=1<<K;
  for(i=0;i<n;i++)for(j=0;j<n;j++)read(w[i][j]);
  m=1;
  while(K--){
    m2=m;m<<=1;
    for(l=0;l<n;l+=m){
      r=l+m,mid=l+m2;
      for(x=l;x<mid;x++)for(z=mid;z<r;z++){
        g[x][z]=inf;
        for(y=l;y<mid;y++)up(g[x][z],f[x][y]+w[y][z]);
      }
      for(x=mid;x<r;x++)for(z=l;z<mid;z++){
        g[x][z]=inf;
        for(y=mid;y<r;y++)up(g[x][z],f[x][y]+w[y][z]);
      }
      for(x=l;x<mid;x++)for(z=mid;z<r;z++){
        h[x][z]=inf;
        for(y=mid;y<r;y++)up(h[x][z],g[x][y]+f[y][z]);
      }
      for(x=mid;x<r;x++)for(z=l;z<mid;z++){
        h[x][z]=inf;
        for(y=l;y<mid;y++)up(h[x][z],g[x][y]+f[y][z]);
      }
      for(x=l;x<r;x++)for(y=l;y<r;y++)f[x][y]=inf;
      for(x=l;x<mid;x++)for(y=mid;y<r;y++)f[x][y]=h[x][y];
      for(x=mid;x<r;x++)for(y=l;y<mid;y++)f[x][y]=h[x][y];
    }
  }
  ans=inf;
  for(i=0;i<n;i++)for(j=0;j<n;j++)up(ans,f[i][j]);
  return printf("%d",ans),0;
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值