洛谷P1070道路遊戲題解--zhengjun

題面傳送門

思路

首先,這道題一定是個dpdp,因爲題中說一旦機器人走到頭了,就要立刻在其他任意的一個機器人工廠買。

一開始弄得fi,jf_{i,j}是到了第ii個工廠,用了jj個時間,機器人已經走到頭了的最大金幣數,然後一想,似乎不需要前面這一個維度,(我要你有何用),反正下一次都可以在任意的地方幹嘛還要這樣嘛。

所以就用fif_i表示用了ii個時間,機器人已經走到頭了的最大金幣數,轉移公式就是:

fi=max{fik+sum(i,j,k)ajk},sumf_i=\max\{f_{i-k}+sum(i,j,k)-a_{j-k}\},sum就是在jj分鐘時從ii個工廠開始向後走kk個工廠路上可以得到的價值。

然後,就在想怎麼求這個奇怪的sumsum,呦吼,前綴和是個好東西,用前綴和表示,現在用了jj分鐘走到了ii個工廠所得到的金幣數,那麼,不就可以遞推求解了嗎?這裏要注意一下是個環,我習慣11開頭了,改不掉。

所以,先用tt數組存下每條道路每個時間的金幣數

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

這個lastlast因爲我是從11開始的,所以有點麻煩。

因爲最後要從11開始,所以最後取模完要加一個11,那麼,在取模之前就要減一個11,然後就是這樣lasti=(i11+n)modn+1last_i=(i-1-1+n)\bmod n+1

如果要從ii號點開始,往前kk個點,就是((ik1)modn+n)modn((i-k-1)\bmod n+n)\bmod n

好了,複雜度O(n3)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;
}

然後,發現剛纔那個式子其實就是(爲了看得清楚,環先不考慮):

fi=max{fik+sumj,isumjk,ikajk}f_i=\max\{f_{i-k}+sum_{j,i}-sum_{j-k,i-k}-a_{j-k}\}

=max{fiksumjk,ikajk}+sumj,i=\max\{f_{i-k}-sum_{j-k,i-k}-a_{j-k}\}+sum_{j,i}

這一坨k-k\cdots,發現好像可以用隊列維護。

qi,jq_{i,j}維護fisumj,iajf_{i}-sum_{j,i}-a_{j},然後複雜度就成了O(n2)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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章