【洛谷5052】[COCI2017-2018#7] Go(区间DP)

点此看题面

  • (n)个房子和(m)个奖励,第(i)个奖励形如在第(ti_i)个时刻前到达第(a_i)个房子((a_i)互不相同)可以获得(v_i)的收益。
  • 求从第(k)个房子出发能获得的最大总收益。
  • (kle nle10^3,mle100,ti_ile2 imes10^3)

区间(DP)

由于我们走过的范围必然是包含起点(k)的一个区间,且我们肯定只会选择在某个奖励屋回头,因此可以设(f_{i,j,t,0/1})表示走到过第(isim j)个奖励屋,当前时刻为(t),正处于左/右端点时的最大收益。

注意,如果起点(k)不是奖励屋,方便起见可以在起点随便设个奖励。

转移时只需考虑从哪个起点出发向哪个方向扩展一步,共四种情况简单讨论一下即可。

代码:(O(m^2t))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define M 100
#define T 2000
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,m,k,a[M+5],v[M+5],ti[M+5],f[M+5][M+5][T+5][2];
int main()
{
	RI i,j,t,p;for(scanf("%d%d%d",&n,&k,&m),i=1;i<=m;++i) scanf("%d%d%d",a+i,v+i,ti+i);
	for(i=m+1;a[i-1]>=k;--i);if(a[i]^k) {for(j=++m;j^i;--j) a[j]=a[j-1],v[j]=v[j-1],ti[j]=ti[j-1];a[i]=k,v[i]=0;}k=i;//强制k为奖励屋
	for(i=k;i;--i) for(j=k;j<=m;++j) for(t=0;t<=T;++t) f[i][j][t][0]=f[i][j][t][1]=-1e9;f[k][k][0][0]=v[k];//初始化
	RI ans=0;for(i=k;i;--i) for(j=k;j<=m;++j) for(t=0;t<=T;++t) Gmax(ans,f[i][j][t][0]),Gmax(ans,f[i][j][t][1]),//更新答案
		i^1&&t+a[i]-a[i-1]<=T&&Gmax(f[i-1][j][t+a[i]-a[i-1]][0],f[i][j][t][0]+(t+a[i]-a[i-1]<ti[i-1]?v[i-1]:0)),//从左端点向左扩一步
		i^1&&t+a[j]-a[i-1]<=T&&Gmax(f[i-1][j][t+a[j]-a[i-1]][0],f[i][j][t][1]+(t+a[j]-a[i-1]<ti[i-1]?v[i-1]:0)),//从右端点向左扩一步
		j^m&&t+a[j+1]-a[i]<=T&&Gmax(f[i][j+1][t+a[j+1]-a[i]][1],f[i][j][t][0]+(t+a[j+1]-a[i]<ti[j+1]?v[j+1]:0)),//从左端点向右扩一步
		j^m&&t+a[j+1]-a[j]<=T&&Gmax(f[i][j+1][t+a[j+1]-a[j]][1],f[i][j][t][1]+(t+a[j+1]-a[j]<ti[j+1]?v[j+1]:0));//从右端点向右扩一步
	return printf("%d
",ans),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5052.html