1571. [Usaco2009 Open]滑雪课Ski

传送门

可以想到 $dp$,设 $f[i][j]$ 表示当前等级为 $i$,时间为 $j$ 的最大滑雪次数

显然上课不会上让自己等级降低的课,所以第一维 $i$ 满足无后效性

然后直接枚举 $i,j$,对于每个时间 $j$ ,考虑选择滑雪,因为划不同的坡得到的价值都是 $1$,所以直接取当前能划的时间最少的坡就行了

每个时间 $j$ 枚举完以后再考虑上课,只要考虑能增加等级的课就好了

注意不合法的状态是 $-INF$

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
typedef long long ll;
using namespace std;
typedef long long ll;
inline ll read()
{
    ll x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=1e4+7,M=107,INF=1e9+7;
int T,n,m,f[M][N],ans;
struct Class{
    int s,t,a;
}C[M];
struct Hillside{
    int tim,lev;
    inline bool operator < (const Hillside &tmp) const {
        return lev<tmp.lev;
    }
}H[N];
int main()
{
    T=read(),n=read(),m=read(); int a,b,c,mxlev=1;
    for(int i=1;i<=n;i++)
    {
        a=read(),b=read(),c=read();
        C[i]=(Class){a,b,c}; mxlev=max(mxlev,c);
    }
    for(int i=1;i<=m;i++)
        H[i].lev=read(),H[i].tim=read();
    sort(H+1,H+m+1);
    memset(f,~0x3f,sizeof(f));//不合法默认-INF
    f[1][0]=0;
    int L=0,tim=INF;//tim是目前耗时最小的坡需要的时间
    for(int i=1;i<=mxlev;i++)
    {
        while(H[L+1].lev<=i&&L<m) L++,tim=min(tim,H[L].tim);//坡已经按等级排序了
        for(int j=0;j<=T;j++)
        {
            if(j+tim<=T) f[i][j+tim]=max(f[i][j+tim],f[i][j]+1);//滑雪,注意时间不能超过T
            f[i][j+1]=max(f[i][j+1],f[i][j]);/*当然也可以什么都不做*/ ans=max(ans,f[i][j]);//更新ans
        }
        for(int j=1;j<=n;j++)
            if(C[i].a>i&&C[i].s+C[i].t<=T)//只考虑能增加等级的课,时间也要合法
                f[C[i].a][C[i].s+C[i].t]=max(f[C[i].a][C[i].s+C[i].t],f[i][C[i].s]);//上课
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2019-08-09 13:42  LLTYYC  阅读(157)  评论(0编辑  收藏  举报