洛谷P1070道路游戏题解--zhengjun

Ophira ·
更新时间:2024-09-21
· 955 次阅读

题面传送门

思路

首先,这道题一定是个dpdpdp,因为题中说一旦机器人走到头了,就要立刻在其他任意的一个机器人工厂买。

一开始弄得fi,jf_{i,j}fi,j​是到了第iii个工厂,用了jjj个时间,机器人已经走到头了的最大金币数,然后一想,似乎不需要前面这一个维度,(我要你有何用),反正下一次都可以在任意的地方干嘛还要这样嘛。

所以就用fif_ifi​表示用了iii个时间,机器人已经走到头了的最大金币数,转移公式就是:

fi=max⁡{fi−k+sum(i,j,k)−aj−k},sumf_i=\max\{f_{i-k}+sum(i,j,k)-a_{j-k}\},sumfi​=max{fi−k​+sum(i,j,k)−aj−k​},sum就是在jjj分钟时从iii个工厂开始向后走kkk个工厂路上可以得到的价值。

然后,就在想怎么求这个奇怪的sumsumsum,呦吼,前缀和是个好东西,用前缀和表示,现在用了jjj分钟走到了iii个工厂所得到的金币数,那么,不就可以递推求解了吗?这里要注意一下是个环,我习惯111开头了,改不掉。

所以,先用ttt数组存下每条道路每个时间的金币数

sumi,j=sumlasti,j−1+tlasti,jsum_{i,j}=sum_{last_i,j-1}+t_{last_i,j}sumi,j​=sumlasti​,j−1​+tlasti​,j​

这个lastlastlast因为我是从111开始的,所以有点麻烦。

因为最后要从111开始,所以最后取模完要加一个111,那么,在取模之前就要减一个111,然后就是这样lasti=(i−1−1+n) mod n+1last_i=(i-1-1+n)\bmod n+1lasti​=(i−1−1+n)modn+1

如果要从iii号点开始,往前kkk个点,就是((i−k−1) mod n+n) mod n((i-k-1)\bmod n+n)\bmod n((i−k−1)modn+n)modn

好了,复杂度O(n3)O(n^3)O(n3),有点勉强

#include #define max(x,y) ((x)>(y)?(x):(y)) using namespace std; int n,m,p; int a[1001]; int t[1001][1001]; int sum[1001][1001]; int f[1001]; int main(){ scanf("%d%d%d",&n,&m,&p); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ scanf("%d",&t[i][j]); } } for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int j=1;j<=m;j++){ for(int i=1;i<=n;i++){ sum[i][j]=sum[(i-1-1+n)%n+1][j-1]+t[(i-1-1+n)%n+1][j]; } } for(int i=1;i<=m;i++){ f[i]=-0x3fffffff; for(int j=1;j<=n;j++){ for(int k=1;k<=p&&k<=i;k++){ int last=((j-k-1)%n+n)%n+1; f[i]=max(f[i],f[i-k]+sum[j][i]-sum[last][i-k]-a[last]); } } } printf("%d",f[m]); return 0; }

然后,发现刚才那个式子其实就是(为了看得清楚,环先不考虑):

fi=max⁡{fi−k+sumj,i−sumj−k,i−k−aj−k}f_i=\max\{f_{i-k}+sum_{j,i}-sum_{j-k,i-k}-a_{j-k}\}fi​=max{fi−k​+sumj,i​−sumj−k,i−k​−aj−k​}

=max⁡{fi−k−sumj−k,i−k−aj−k}+sumj,i=\max\{f_{i-k}-sum_{j-k,i-k}-a_{j-k}\}+sum_{j,i}=max{fi−k​−sumj−k,i−k​−aj−k​}+sumj,i​

这一坨−k⋯-k\cdots−k⋯,发现好像可以用队列维护。

用qi,jq_{i,j}qi,j​维护fi−sumj,i−ajf_{i}-sum_{j,i}-a_{j}fi​−sumj,i​−aj​,然后复杂度就成了O(n2)O(n^2)O(n2),终于没了。

代码 #include #define max(x,y) ((x)>(y)?(x):(y)) using namespace std; int n,m,p; int a[1001]; int t[1001][1001]; int sum[1001][1001]; int f[1001]; int q[1001][1001],k[1001][1001],head[1001],tail[1001]; int main(){ scanf("%d%d%d",&n,&m,&p); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&t[i][j]); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); q[i][0]=-a[i]; } for(int j=1;j<=m;j++) for(int i=1;i<=n;i++) sum[i][j]=sum[(i-1-1+n)%n+1][j-1]+t[(i-1-1+n)%n+1][j]; for(int i=1;i<=m;i++){ f[i]=-0x3fffffff; for(int j=1;j<=n;j++){ int l=((j-i-1)%n+n)%n+1; while(head[l]<=tail[l]&&k[l][head[l]]+p<i)head[l]++; if(head[l]<=tail[l])f[i]=max(f[i],q[l][head[l]]+sum[j][i]); }//这部分是找到最佳的一个k,然后下面是更新序列,注意千万不能并在一起,因为f[i]还没有更新完 for(int j=1;j<=n;j++){ int l=((j-i-1)%n+n)%n+1; int x=f[i]-sum[j][i]-a[j]; while(head[l]<=tail[l]&&q[l][tail[l]]<=x)tail[l]--; k[l][++tail[l]]=i; q[l][tail[l]]=x; } } printf("%d",f[m]); return 0; } 谢谢–zhengjun
作者:A_zjzj



p1

需要 登录 后方可回复, 如果你还没有账号请 注册新账号