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

题面传送门

思路

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

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

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

f i = max { f i k + s u m ( i , j , k ) a j k } , s u m f_i=\max\{f_{i-k}+sum(i,j,k)-a_{j-k}\},sum 就是在 j j 分钟时从 i i 个工厂开始向后走 k k 个工厂路上可以得到的价值。

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

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

s u m i , j = s u m l a s t i , j 1 + t l a s t i , j sum_{i,j}=sum_{last_i,j-1}+t_{last_i,j}

这个 l a s t last 因为我是从 1 1 开始的,所以有点麻烦。

因为最后要从 1 1 开始,所以最后取模完要加一个 1 1 ,那么,在取模之前就要减一个 1 1 ,然后就是这样 l a s t i = ( i 1 1 + n ) m o d n + 1 last_i=(i-1-1+n)\bmod n+1

如果要从 i i 号点开始,往前 k k 个点,就是 ( ( i k 1 ) m o d n + n ) m o d n ((i-k-1)\bmod n+n)\bmod n

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

#include<bits/stdc++.h>
#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;
}

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

f i = max { f i k + s u m j , i s u m j k , i k a j k } f_i=\max\{f_{i-k}+sum_{j,i}-sum_{j-k,i-k}-a_{j-k}\}

= max { f i k s u m j k , i k a j k } + s u m j , i =\max\{f_{i-k}-sum_{j-k,i-k}-a_{j-k}\}+sum_{j,i}

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

q i , j q_{i,j} 维护 f i s u m j , i a j f_{i}-sum_{j,i}-a_{j} ,然后复杂度就成了 O ( n 2 ) O(n^2) ,终于没了。

代码

#include<bits/stdc++.h>
#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

原创文章 115 获赞 118 访问量 6511

猜你喜欢

转载自blog.csdn.net/A_zjzj/article/details/105638794