[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;
    }
    
posted @ 2020-12-02 08:29  clockwhite  阅读(105)  评论(0编辑  收藏  举报