zoukankan      html  css  js  c++  java
  • luogu P1070 道路游戏

    传送门

    这里设(f_i)表示时刻(i)的答案

    转移的话在([i-p+1,i-1])之间枚举j,然后考虑从哪个点走过来

    复杂度为(O(n^3))

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define LL long long
    #define il inline
    #define re register
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define inf 999999999
    
    using namespace std;
    const int N=1000+10;
    il LL rd()
    {
        re LL x=0,w=1;re char ch=0;
        while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
        return x*w;
    }
    int f[N],a[N][N],c[N],n,m,p;
    
    int main()
    {
      n=rd(),m=rd(),p=rd();
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          a[(i-j+n*m)%n+1][j]=rd();
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          a[i][j]+=a[i][j-1];
      for(int i=1;i<=n;i++) c[i]=rd();
      memset(f,-63,sizeof(f));
      f[0]=0;
      for(int i=1;i<=m;i++)
        for(int j=max(0,i-p);j<i;j++)
          {
            int cn=-inf;
            for(int k=1;k<=n;k++) cn=max(cn,a[(k-i+n*m)%n+1][i]-a[(k-i+n*m)%n+1][j]-c[(k-i+j+n*m)%n+1]);
            f[i]=max(f[i],f[j]+cn);
          }
      printf("%d
    ",f[m]);
      return 0;
    }
    

    但是发现重新选择的次数越多,负价值似乎也越多,考虑重设(f_{i,j})表示时刻(i)走到(j)的答案,转移要么这一点重新选一个机器人,要么从之前的合法的(f_{i-1,(j-1)mod n+1})转移

    // luogu-judger-enable-o2
    /*省略*/
    
    using namespace std;
    const int N=1000+10;
    il LL rd()
    {
        /*省略*/
    }
    int F[N],f[N][N],ti[N][N],a[N][N],la[N],c[N],n,m,p;	//la[i]=i前一个位置
    
    int main()
    {
      n=rd(),m=rd(),p=rd();
      for(int j=2;j<=n;j++) la[j]=j-1;
      la[1]=n;
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          a[i][j]=rd();
      memset(f,-63,sizeof(f));
      memset(F,-63,sizeof(F));
      for(int j=1;j<=n;j++) c[j]=rd();
      for(int j=1;j<=n;j++)
        {
          ti[1][j]=1,f[1][j]=a[la[j]][1]-c[la[j]];
          F[1]=max(F[1],f[1][j]);
        }
      for(int i=2;i<=m;i++)
        for(int j=1;j<=n;j++)
          {
            ti[i][j]=1,f[i][j]=F[i-1]-c[la[j]];
            if(ti[i-1][la[j]]<p&&f[i][j]<f[i-1][la[j]]) ti[i][j]=ti[i-1][la[j]]+1,f[i][j]=f[i-1][la[j]];
            f[i][j]+=a[la[j]][i];
            F[i]=max(F[i],f[i][j]);
          }
      printf("%d
    ",F[m]);
      return 0;
    }
    

    乍一看好像是对的

    但是会被下面这个数据(mathfrak{X})掉(huaji)

    5 4 3
    1 1 1 138
    1 1 1 1
    1 1 1 1
    1 1 1 1
    1 1 1 1
    233 66 66 66 66
    

    因为上一个代码转移是第一个用3s,第二个用1s,但最优策略是两个都用2s(color{#FFFFFF}{-1s&+1s})

    考虑如果这样转移

    [f_{i,j}=max(F_{i-k}+a_{i,j}-a_{i-k,j-k}-c_{j-k+1})$$(为了简便不取膜) 其中$a_{i,j}$为**$i$时刻到$j$位置**的前缀和 ```cpp for(int j=1;j<=n;j++) for(int i=1;i<=m;i++) a[i][j]=rd(); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) a[i][j]+=a[i-1][(j-1+n-1)%n+1]; ``` 把$a_{i,j}$拎出去 $$f_{i,j}=max(F_{i-k}-a_{i-k,j-k}-c_{j-k+1})+a_{i,j}]

    (b_{i,j}=F_{i}-a_{i,j}-c_{j+1})

    则$$f_{i,j}=max(b_{i-k,j-k})+a_{i,j}$$

    k的范围为([1,n]),所以相当于是滑动的区间最大值虽然是斜的,单调队列即可

    代码

    // luogu-judger-enable-o2
    /*省略*/
    
    using namespace std;
    const int N=1000+10;
    il LL rd()
    {
        /*省略*/
    }
    int F[N],f[N][N],a[N][N],c[N],nt[N],n,m,p;	//nt[i]=i后一个位置
    int q[N][N],hd[N],ti[N][N],tl[N];
    
    int main()
    {
      n=rd(),m=rd(),p=rd();
      for(int i=1;i<n;i++) nt[i]=i+1;
      nt[n]=1;
      for(int j=1;j<=n;j++)
        for(int i=1;i<=m;i++)
          a[i][j]=rd();
      for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
          a[i][j]+=a[i-1][(j-1+n-1)%n+1];
      for(int i=1;i<=n;i++) c[i]=rd();
      memset(F,-63,sizeof(F));
      memset(f,-63,sizeof(f));
      F[0]=0;for(int i=1;i<=n;i++) hd[i]=tl[i]=1,q[i][1]=-c[i];
      for(int j=1;j<=n;j++)
        {
          f[1][j]=a[1][j]-c[j];
          F[1]=max(F[1],f[1][j]);
        }
      for(int j=1;j<=n;j++)
        {
          int b=F[1]-a[1][j]-c[nt[j]];
          while(hd[j]<=tl[j]&&b>=q[j][tl[j]]) --tl[j];
          q[j][++tl[j]]=b,ti[j][tl[j]]=1;
        }
      for(int i=2;i<=m;i++)
        {
          for(int j=1,ii=(1-i+n*m)%n+1;j<=n;j++,ii=nt[ii])
            {
              while(hd[ii]<=tl[ii]&&ti[ii][hd[ii]]+p<i) ++hd[ii];
              f[i][j]=q[ii][hd[ii]]+a[i][j];
              F[i]=max(F[i],f[i][j]);
            }
          for(int j=1,ii=(1-i+n*m)%n+1;j<=n;j++,ii=nt[ii])
            {
              int b=F[i]-a[i][j]-c[nt[j]];
              while(hd[ii]<=tl[ii]&&b>=q[ii][tl[ii]]) --tl[ii];
              q[ii][++tl[ii]]=b,ti[ii][tl[ii]]=i;
            }
        }
      printf("%d
    ",F[m]);
      return 0;
    }
    
    
  • 相关阅读:
    使用visio 2007对现有的数据库进行反向工程
    GOOGLE地图坐标拾取方法、GOOGLE地图获取坐标方法
    Visio 2007中进行数据库建模时如何显示字段类型以及概念名称
    WCF把书读薄(4)——事务编程与可靠会话
    WCF把书读薄(3)——数据契约、消息契约与错误契约
    WCF把书读薄(2)——消息交换、服务实例、会话与并发
    Backbone.js developer 武汉 年薪8w-10w
    Java面试
    从pb文件中恢复计算图并在tensorboard中展示
    Ubuntu下解决u盘变成只读模式
  • 原文地址:https://www.cnblogs.com/smyjr/p/9683033.html
Copyright © 2011-2022 走看看