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

[USACO19DEC]Greedy Pie Eaters P 題解

題目連結https://www.luogu.com.cn/problem/P5851

題意:

\(N\) 個派, \(M\) 只奶牛。第 \(i\) 只奶牛體重為 \(W_i\)\([L_i,R_i]\) 中的,每次吃都會吧這個區間中所有剩下的吃完。求選擇一些奶牛且恰當安排順序使得吃到派的奶牛的體重之和最大。 \((1\le L_i,R_i\le N\le300,1\le M\le\frac{N(N-1)}{2},1\le W_i\le10^6)\)

題解:

每次考慮通過一個被吃光的連續區間來轉移(其實沒被吃光也無所謂,反正不選就是了)。

因此設 \(F(i,j)\) 為已經選完 \([i,j]\)

的最大體重。

區間 DP 的常見套路:列舉中間端點:

  • \(F(i,j)=\max\left\{F(i,k)+F(k+1,j)\right\}(i\le k<j)\)

  • \(F(i,j)=\max\left\{F(i,k-1)+F(k+1,j)+A(i,j,k)\right\}(1\le j\le k)\)

其中 \(A(i,j,k)\) 表示被包含在 \([i,j]\) 中,幷包含 \(k\) 這個點的最大奶牛體重。

所以怎麼求這個東西呢?又可以採用區間 DP 的套路,對於 \([i,j]\) ,從 \([i+1,j]\)\([i,j-1]\) 轉移。

程式碼非常清晰,可以直接看。時間複雜度: \(O(N^3)\)

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int N=305;
int n,m,f[N][N],a[N][N][N];
signed main(){
	scanf("%d%d",&n,&m);
	while(m--){
		int w,l,r;scanf("%d%d%d",&w,&l,&r);
		for(int i=l;i<=r;i++) a[l][r][i]=w;
	}
	for(int i=n;i;i--)
		for(int j=i;j<=n;j++){
			if(i!=1) for(int k=i;k<=j;k++) a[i-1][j][k]=max(a[i-1][j][k],a[i][j][k]);
			if(j!=n) for(int k=i;k<=j;k++) a[i][j+1][k]=max(a[i][j+1][k],a[i][j][k]);
		}
	for(int i=n;i;i--)
		for(int j=i;j<=n;j++){
			for(int k=i;k<j;k++) f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
			for(int k=i;k<=j;k++) f[i][j]=max(f[i][j],f[i][k-1]+f[k+1][j]+a[i][j][k]);
		}
	printf("%d\n",f[1][n]);
	return 0;
}