2012-0030

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

题目描述写的不够好,抱歉。 这里再简要说明一下各个量的意义,n事件数量,t正常清醒时间,k正常睡眠时间,l极限熬夜长度,si第i件事情的开始时刻,ei第i件事情的结束时刻,vi第i件事情的价值。
每天睡眠者的清醒时间可以是t, t+1, t+2, ..., t+l,对应的睡眠时间依次是k, k+1, k+2, ..., k+l,对应的熬夜惩罚依次是0, -1^2^, -2^2^, ..., -l^2^。(熬夜惩罚对一天只计算一次)

一个dp,f[i][j]表示在i时刻已经清醒了j个时间单位的时候最大的满意度。

这里不妨设|x|=x(x>0),0(x<=0)

转移方程:

f[i][j]=max{f[i - 1][j - 1] - |j - t|^2^ + |j - 1 - t|^2^ , f[s[k]][j - (e[k] - s[k])] + v[k] - |j - t|^2^ + |j - (e[k] - s[k]) - t|^2^  (e[k] == i, j - (e[k] - s[k]) >= 0)} (j > 0)

f[i][0]=max{f[i - k - dt][t + dt](0 <= dt <= l)}(保证下标有意义即可)

边界条件:

f[0][0]=0;

f[0][i]=-∞;(i>0) 也就是无法在0时刻清醒i时间单位。

{{{
/*
 * File:   Solution.cpp
 * Author: Bob
 *
 * Created on 2012年6月25日, 下午9:37
 */

#include <cstdlib>
#include <cstdio>
#include <cassert>

int const max=10000;
int const maxn=1000;
int const maxt=100;
int const maxk=50;
int const maxl=20;
int const maxv=500;

void swap(int &a, int &b){
    int t=a;
    a=b;
    b=t;
}

int cal(int a){
    if(a>0)return a*a; else return 0;
}

void solution(){
    int f[max+1][maxt+maxl+1],s[maxn+1],e[maxn+1],v[maxn+1];
    int n,t,k,l;
    scanf("%d%d%d%d",&n,&t,&k,&l);
    assert(n>=0 && n<=maxn);
    assert(t>=1 && t<=maxt);
    assert(k>=1 && k<=maxk);
    assert(l>=0 && l<=maxl);
    int maxe=0;
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&s[i],&e[i],&v[i]);
        //printf("%d %d %d\n",s[i],e[i],v[i]);
        assert(s[i]>=0);
        assert(e[i]>s[i] && e[i]<=max);
        assert(v[i]>=1 && v[i]<=maxv);
        if(e[i]>maxe)maxe=e[i];
    }
    for(int i=1;i<n;i++){
        for(int j=i+1;j<=n;j++){
            if((e[i]>e[j])||(e[i]==e[j] && s[i]<s[j])){
                swap(e[i],e[j]);
                swap(s[i],s[j]);
                swap(v[i],v[j]);
            }
        }
    }
    //for(int i=1;i<n;i++)printf("%d ",e[i]);
    //printf("%d %d\n",e[n],maxe);
    int p=1;
    f[0][0]=0;
    for(int i=1;i<=t+l;i++){
        f[0][i]=-10000000;
    }
    int ans=0;
    for(int i=1;i<=maxe;i++){
        f[i][0]=-10000000;//相当于设置成无法到这一状态
        for(int dt=0;dt<=l;dt++){
            int tmp=i-k-dt;
            if(tmp>=0){
                tmp=f[tmp][t+dt];
                if(tmp>f[i][0])f[i][0]=tmp;
            }
        }
        if(f[i][0]>ans)ans=f[i][0];
        //printf("#%d: %d ",i,f[i][0]);
        for(int j=1;j<=t+l;j++){
            f[i][j]=f[i-1][j-1]-cal(j-t)+cal(j-t-1);
            int tmp=0;//同一时间可能有多个事件结束,用tmpp缓存
            for(int tmpp=p;tmpp<=n && e[tmpp]==i && j>=e[tmpp]-s[tmpp];tmpp++){
                tmp=f[s[tmpp]][j-(e[tmpp]-s[tmpp])]+v[tmpp]-cal(j-t)+cal(j-(e[tmpp]-s[tmpp])-t); //之前已经计算过的熬夜惩罚要补上
                if(tmp>f[i][j])f[i][j]=tmp;
            }
            //printf("%d ",f[i][j]);
            if(f[i][j]>ans)ans=f[i][j];
        }
        //printf("\n");
        while(p<=n && e[p]==i)p++;//该循环结束,将p指向结束时间大于i的事件
    }
    printf("%d\n",ans);
}

int main() {
    int c;
    scanf("%d",&c);
    for(int i=0;i<c;i++){
        solution();
    }
    return 0;
}
}}}

题目描述写的不够好,抱歉。 这里再简要说明一下各个量的意义,n事件数量,t正常清醒时间,k正常睡眠时间,l极限熬夜长度,si第i件事情的开始时刻,ei第i件事情的结束时刻,vi第i件事情的价值。

每天睡眠者的清醒时间可以是t, t+1, t+2, ..., t+l,对应的睡眠时间依次是k, k+1, k+2, ..., k+l,对应的熬夜惩罚依次是0, -12, -22, ..., -l2。(熬夜惩罚对一天只计算一次)

一个dp,f[i][j]表示在i时刻已经清醒了j个时间单位的时候最大的满意度。

这里不妨设|x|=x(x>0),0(x<=0)

转移方程:

f[i][j]=max{f[i - 1][j - 1] - |j - t|2 + |j - 1 - t|2 , f[s[k]][j - (e[k] - s[k])] + v[k] - |j - t|2 + |j - (e[k] - s[k]) - t|2 (e[k] == i, j - (e[k] - s[k]) >= 0)} (j > 0)

f[i][0]=max{f[i - k - dt][t + dt](0 <= dt <= l)}(保证下标有意义即可)

边界条件:

f[0][0]=0;

f[0][i]=-∞;(i>0) 也就是无法在0时刻清醒i时间单位。

/*
 * File:   Solution.cpp
 * Author: Bob
 *
 * Created on 2012年6月25日, 下午9:37
 */
#include <cstdlib>
#include <cstdio>
#include <cassert>
int const max=10000;
int const maxn=1000;
int const maxt=100;
int const maxk=50;
int const maxl=20;
int const maxv=500;
void swap(int &a, int &b){
    int t=a;
    a=b;
    b=t;
}
int cal(int a){
    if(a>0)return a*a; else return 0;
}
void solution(){
    int f[max+1][maxt+maxl+1],s[maxn+1],e[maxn+1],v[maxn+1];
    int n,t,k,l;
    scanf("%d%d%d%d",&n,&t,&k,&l);
    assert(n>=0 && n<=maxn);
    assert(t>=1 && t<=maxt);
    assert(k>=1 && k<=maxk);
    assert(l>=0 && l<=maxl);
    int maxe=0;
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&s[i],&e[i],&v[i]);
        //printf("%d %d %d\n",s[i],e[i],v[i]);
        assert(s[i]>=0);
        assert(e[i]>s[i] && e[i]<=max);
        assert(v[i]>=1 && v[i]<=maxv);
        if(e[i]>maxe)maxe=e[i];
    }
    for(int i=1;i<n;i++){
        for(int j=i+1;j<=n;j++){
            if((e[i]>e[j])||(e[i]==e[j] && s[i]<s[j])){
                swap(e[i],e[j]);
                swap(s[i],s[j]);
                swap(v[i],v[j]);
            }
        }
    }
    //for(int i=1;i<n;i++)printf("%d ",e[i]);
    //printf("%d %d\n",e[n],maxe);
    int p=1;
    f[0][0]=0;
    for(int i=1;i<=t+l;i++){
        f[0][i]=-10000000;
    }
    int ans=0;
    for(int i=1;i<=maxe;i++){
        f[i][0]=-10000000;//相当于设置成无法到这一状态
        for(int dt=0;dt<=l;dt++){
            int tmp=i-k-dt;
            if(tmp>=0){
                tmp=f[tmp][t+dt];
                if(tmp>f[i][0])f[i][0]=tmp;
            }
        }
        if(f[i][0]>ans)ans=f[i][0];
        //printf("#%d: %d ",i,f[i][0]);
        for(int j=1;j<=t+l;j++){
            f[i][j]=f[i-1][j-1]-cal(j-t)+cal(j-t-1);
            int tmp=0;//同一时间可能有多个事件结束,用tmpp缓存
            for(int tmpp=p;tmpp<=n && e[tmpp]==i && j>=e[tmpp]-s[tmpp];tmpp++){
                tmp=f[s[tmpp]][j-(e[tmpp]-s[tmpp])]+v[tmpp]-cal(j-t)+cal(j-(e[tmpp]-s[tmpp])-t); //之前已经计算过的熬夜惩罚要补上
                if(tmp>f[i][j])f[i][j]=tmp;
            }
            //printf("%d ",f[i][j]);
            if(f[i][j]>ans)ans=f[i][j];
        }
        //printf("\n");
        while(p<=n && e[p]==i)p++;//该循环结束,将p指向结束时间大于i的事件
    }
    printf("%d\n",ans);
}
int main() {
    int c;
    scanf("%d",&c);
    for(int i=0;i<c;i++){
        solution();
    }
    return 0;
}