bzoj 1481 Navigation Game 决策单调dp

f[i][j][k(0/1)] 表示现在在点(i,j),在第i行只走了j一个点,经过的 F 个数奇偶性为k时最少时间。
l[i][j(0/1)][k(0/1)] 表示在当前行,从i左边的点t出发,经过j个 F ,由 f[pre][t][k] 更新来的最小时间。
r[i][j(0/1)][k(0/1)] 表示在当前行,从i右边的点t出发,经过j个 F ,由 f[pre][t][k] 更新来的最小时间。
然后 l r 满足决策单调性。
就可以通过分治做到 O(n2logn) 的复杂度。

不过这里有个问题,就是分治时当状态区间的中点无法被任何点更新时(无解)下一步取值区间的中点无法选择。
由于数据较水,因此取状态区间的中点可过。。。
还有由于代码写得太渣,自带了一个大常数。。。

#include <bits/stdc++.h>
using namespace std;
#define N 1100
#define cl(x) memset(x,0x3f,sizeof(x))
int n,m,ans,inf;
int ln,rn,vn,sn,no,nb,ns,nn,now,dir;
char s[N][N];
int f[N][N][2],l[N][2][2],r[N][2][2];
int cal(int x,int y)
{
    if(s[x][y]=='B')return 0;
    if(s[x][y]=='S')return 2;
    return 1;
}
void upd(int &x,int y){x=min(x,y);}
void add(char c,int tp)
{
    if(c=='B')nb+=tp;else if(c=='S')ns+=tp;else nn+=tp;
    if(c=='O')no+=tp;if(c=='F')sn^=1;   
}
void del(int x,int tp)
{
    char c=s[now][x];
    if(tp==dir)
        vn-=nn+ns*2,add(c,-1);
    else
    {
        add(c,-1);
        if(c=='S')vn-=(nn+ns+nb+1)*2;
        else if(c!='B')vn-=nn+ns+nb+1;
    }
}
void ins(int x,int tp)
{
    char c=s[now][x];
    if(tp==dir)
        add(c,1),vn+=nn+ns*2;
    else
    {
        if(c=='S')vn+=(nn+ns+nb+1)*2;
        else if(c!='B')vn+=nn+ns+nb+1;
        add(c,1);
    }
}
void move(int l,int r)
{
    while(ln<l)del(ln,0),ln++;
    while(ln>l)ln--,ins(ln,0);
    while(rn>r)del(rn,1),rn--;
    while(rn<r)rn++,ins(rn,1);
}
void solvel(int l1,int r1,int l2,int r2,int tp,int tp1)
{
    if(l1>r1||l2>r2)return;
    int mid=(l1+r1)>>1,pos=0;
    for(int i=min(mid,r2);i>=l2;i--)
    {
        move(i+1,mid);
        if(!no&&sn==tp1&&l[mid][tp][tp1]>f[now][i][tp^sn]+vn)
        {
            l[mid][tp][tp1]=f[now][i][tp^sn]+vn;
            pos=i;
        }
    }
    if(pos)
    {
        solvel(l1,mid-1,l2,pos,tp,tp1);
        solvel(mid+1,r1,pos,r2,tp,tp1);
    }
    else
    {
        solvel(l1,mid-1,l2,mid-1,tp,tp1);
        solvel(mid+1,r1,mid+1,r2,tp,tp1);
    }
}
void solver(int l1,int r1,int l2,int r2,int tp,int tp1)
{
    if(l1>r1)return;
    int mid=(l1+r1)>>1,pos=0;
    for(int i=max(mid,l2);i<=r2;i++)
    {
        move(mid,i-1);
        if(!no&&sn==tp1&&r[mid][tp][tp1]>f[now][i][tp^sn]+vn)
        {
            r[mid][tp][tp1]=f[now][i][tp^sn]+vn;
            pos=i;
        }
    }
    if(pos)
    {
        solver(l1,mid-1,l2,pos,tp,tp1);
        solver(mid+1,r1,pos,r2,tp,tp1);
    }
    else
    {
        solver(l1,mid-1,l2,mid-1,tp,tp1);
        solver(mid+1,r1,mid+1,r2,tp,tp1);
    }
}
int main()
{
    freopen("tt.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    cl(l);cl(r);cl(f);inf=l[0][0][0];
    for(int i=1;i<=m;i++)
        if(s[n][i]=='H')
            f[n][i][0]=l[i][0][0]=r[i][0][0]=0;
    for(int i=n-1;i>=1;i--)
    {
        for(int j=1;j<=m;j++)
        {
            if(s[i][j]=='O')continue;
            int t=(s[i][j]=='F'),v=cal(i,j);
            upd(f[i][j][t],min(l[j][0][0],l[j][0][1])+v);
            upd(f[i][j][t],min(r[j][0][0],r[j][0][1])+v);
            upd(f[i][j][t^1],min(l[j][1][0],l[j][1][1])+v);
            upd(f[i][j][t^1],min(r[j][1][0],r[j][1][1])+v);
        }
        cl(l);cl(r);now=i;
        ln=1;rn=0;vn=0;sn=0;no=0;nb=0;ns=0;nn=0;dir=0;
        solvel(1,m,1,m,0,0);solvel(1,m,1,m,1,0);
        solvel(1,m,1,m,0,1);solvel(1,m,1,m,1,1);
        ln=1;rn=0;vn=0;sn=0;no=0;nb=0;ns=0;nn=0;dir=1;
        solver(1,m,1,m,0,0);solver(1,m,1,m,1,0);
        solver(1,m,1,m,0,1);solver(1,m,1,m,1,1);
    }
    ans=inf;
    for(int i=1;i<=m;i++)
        if(s[1][i]=='H')
            ans=min(ans,f[1][i][1]);
    if(ans==inf)return puts("Victory of Darkness"),0;
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值