1. 程式人生 > 實用技巧 >P5851 [USACO19DEC]Greedy Pie Eaters P

P5851 [USACO19DEC]Greedy Pie Eaters P

如果只考慮選哪些奶牛吃派和奶牛吃派的順序,就會陷入僵局,那麼我們可以考慮派的情況。

套路地令 \(f_{i,j}\) 表示 \(i\sim j\) 這一段派,能滿足的奶牛的最大可能體重。那麼問題又來了,暴力列舉一頭奶牛轉移過來是 \(n^2m\) 的,怎麼辦呢?

發現奶牛最少吃一個派這個條件可以利用,可以在狀態中記錄這個派的位置。

所以我們再搞一個 \(g\) 陣列,令 \(g_{i,j,k}\) 表示滿足 \(i\leq l\leq k\leq r\leq j\) 的體重最大的奶牛。

顯然這隻奶牛吃派不會影響到 \(i\sim j\) 範圍之外的派,且如果 \(k\) 位置的派還存在這隻奶牛必定能吃。

藉助 \(g\) 陣列後轉移方程很容易就能寫出來:\(f_{i,j}=\max\{f_{i,k-1}+f_{k+1,j}+g_{i,j,k}\}\)

\(g\) 陣列的計算也很簡單,實際上就是預處理一個區間 \(\max\)\(g_{i,j,k}=\max\{g_{i+1,j,k},g_{i,j-1,k}\}\),但是需要注意初始化,即每隻奶牛可以拿 \(l\sim r\) 中的任意一個派保底,\(g_{l,r,l\sim r}=w\)(資料保證奶牛吃派的區間兩兩不同,所以不用取 \(\max\))。

分析一下我們的優化,其實就是通過預處理將列舉奶牛變成了列舉保底的派,使 \(m\) 降到了 \(n\)

時間複雜度 \(O(n^3)\)~

code:

#include<bits/stdc++.h>
using namespace std;
#define N 305
#define Max(x,y)((x)>(y)?x:y)
#define For(i,x,y)for(i=x;i<=(y);i++)
int g[N][N][N],f[N][N];
int main()
{
	int n,m,i,j,w,l,r,len,k;
	scanf("%d%d",&n,&m);
	For(i,1,m)
	{
		scanf("%d%d%d",&w,&l,&r);
		For(j,l,r)g[l][r][j]=w;
	}
	For(len,1,n-1)
	For(i,1,n)
	{
		j=i+len-1;
		if(j>n)break;
		For(k,i,j)g[i][j+1][k]=Max(g[i][j+1][k],g[i][j][k]),g[i-1][j][k]=Max(g[i-1][j][k],g[i][j][k]);
	}
	For(len,1,n)
	For(i,1,n)
	{
		j=i+len-1;
		if(j>n)break;
		For(k,i,j)f[i][j]=Max(f[i][j],f[i][k-1]+f[k+1][j]+g[i][j][k]);
	}
	printf("%d",f[1][n]);
	return 0;
}