[LuoguP2948][USACO09OPEN]Ski Lessons G
P2948 [USACO09OPEN]Ski Lessons G
前言
我已经很久很久,很久很久,没有只依靠自己想出一个DP了。开心。
Solution
首先设计一下状态,\(f_{i,j}\) 表示在时间 i ,能力值为 j 的时候能滑的最多场数。
接下来考虑在第 i 秒能干什么。有以下选项:
-
啥也不干,于是 \(f_{i+1,j}\) 可以被 \(f_{i,j}\) 更新
-
上一节课,\(f_{i+L_k,A_k}\) 可以被 \(f_{i,j}\) 更新,\(k\) 为一个在 i 时间开始的课程。
-
滑一场雪,\(f_{i+D_k,j}\) 可以被 \(f_{i,j}\) 更新,\(k\) 为一个滑雪场。
但是现在时间复杂度很显然不对,到达了 \(O(TVN)\) 的级别,其中 \(V\) 为能力值域。
问题在于枚举一个滑雪场过于冗杂。观察到一个滑雪场可以滑多次,这启发我们贪心,对于一切以当前能力值可以滑的场,总是从中选一个耗时最小的来滑。就可以省去枚举。
而这个耗时最小的场既可以用数据结构维护也可以提前预处理,此处我选择预处理,预处理时间复杂度为\(O(NV)\),总的时间复杂度在 \(O(VN+TV+VS)\), \(VS\) 是因为每堂课会被枚举V次。
初始化为 \(f_{0,1}=0\),其余负无穷。
CODE
#define fe(i,a,b) for(int i=a;i<=b;++i) #define pii pair<int,int> #define mp(a,b) make_pair(a,b) const int inf=1e9; int t,s,n,minn[105],f[20005][105],ans; vector< pii > cl[10005]; inline int M_min(int a,int b){return a<b?a:b;} inline int M_max(int a,int b){return a>b?a:b;} int main(){ t=read(),s=read(),n=read(); fe(i,0,100)minn[i]=inf; fe(i,1,s){ int x=read(),y=read(),z=read(); cl[x].push_back(mp(y,z)); } fe(i,1,n){ int c=read(),d=read(); fe(j,c,100)minn[j]=M_min(minn[j],d); } memset(f,-0x3f,sizeof(f)); f[0][1]=0; fe(i,0,t){ int u=cl[i].size(); fe(j,0,100)if(f[i][j]>=0){ ans=M_max(ans,f[i][j]); fe(k,0,u-1)f[i+cl[i][k].first][cl[i][k].second]=M_max(f[i][j],f[i+cl[i][k].first][cl[i][k].second]); f[i+1][j]=M_max(f[i+1][j],f[i][j]); if(i+minn[j]<=t)f[i+minn[j]][j]=M_max(f[i+minn[j]][j],f[i][j]+1); } } printf("%d",ans); return 0; }