备用钥匙

题目描述

你知道Just Odd Inventions社吗?这个公司的业务是“只不过是奇妙的发明(Just Odd Inventions)”。这里简称为JOI社。
JOI社有N名员工,编号从1到N。所有员工的工作时间从时刻0持续到时刻M,时刻0和时刻M的时候,所有员工都必须在公司内。
某天,出于巧合,JOI社的每个员工都要出行恰好一次。员工i(1<=i<=N)在时刻Si离开公司,时刻Ti回到公司。同一时刻不会同时有两名以上的员工离开或回到公司。
JOI社的入口处有一扇巨大的门,员工只能通过这扇门离开或回到公司。门上挂着一把锁,从公司内部可以任意开锁或上锁,但从公司外部只有持有备用钥匙的人才能开锁或者上锁。时刻0时,锁是锁上的。
每个社员在回到公司的时候,都必须能够进入公司。换句话说,对于任意1<=i<=N,要么员工i持有备用钥匙,要么时刻Ti时门是开着的,否则是不被允许的。员工回到公司的时候,或者携带备用钥匙的员工离开公司的时候,可以选择锁门或不锁。没有携带备用钥匙的员工离开公司的时候没有办法锁门。
JOI社的社长决定把备用钥匙交给N个员工中的K个人。为了避免钥匙的丢失,员工之间不允许借用钥匙。此外,JOI社的社长很重视时间效率,因此每个员工在离开或回到公司的时刻以外,不允许开锁或者上锁。
出于安全的考虑,社长希望上锁的时间越长越好。现在他将员工出入公司的信息和准备交给员工的钥匙数量告诉了你,请你求出在能使所有员工回到公司的时候都能进入公司的大门的前提下,上锁的时间最长是多少。

问题转化

我们把有人进出的时间点刻称为关键时刻。
考虑两个相邻的关键时刻。
如果是两进,那么后一个人有key前一个人进门后就可以锁门。
如果是一进一出,那么肯定可以锁门。
如果是两出,那么前一个人有key就可以锁门。
如果是一出一进,只有两人都有key前一个人才能锁门。
把肯定可以锁门的直接加进答案,然后模型就转化成了这样:
有k把key,i有key可以获得p[i]的价值,同时还存在许多二人关系形如i,j都有key可以获得b[i,j]的价值。
观察特殊性,如果对于一出一进的情况,让前面的人向后面的人连有向边,最后一定会得到若干条链!然后把这样链顺次首尾相接变成一条链。
那么模型为——有k把key,i有key可以获得p[i]的价值,i与i-1都有key可以获得c[i]的价值。
DP即可,然后再随便优化一下。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=2000+10,inf=2000005000;
struct dong{
    int x,id;
    bool p;
};
bool operator < (dong a,dong b){
    return a.x<b.x;
}
dong a[maxn*2];
int p[maxn],id[maxn],c[maxn],right[maxn];
int f[maxn][maxn],g[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,q,ans,top;
int main(){
    freopen("key.in","r",stdin);freopen("key.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    fo(i,1,n){
        scanf("%d%d",&j,&k);
        a[i*2-1].x=j;
        a[i*2-1].p=0;
        a[i*2].x=k;
        a[i*2].p=1;
        a[i*2-1].id=a[i*2].id=i;
    }
    sort(a+1,a+2*n+1);
    ans=a[1].x+m-a[2*n].x;
    fo(i,1,2*n-1)
        if (a[i].p&&!a[i+1].p) ans+=a[i+1].x-a[i].x;
        else if (a[i].p&&a[i+1].p) p[a[i+1].id]+=a[i+1].x-a[i].x;
        else if (!a[i].p&&!a[i+1].p) p[a[i].id]+=a[i+1].x-a[i].x;
        else{
            if (a[i].id==a[i+1].id) p[a[i].id]+=a[i+1].x-a[i].x;
            else{
                right[a[i].id]=a[i+1].id;
                bz[a[i+1].id]=1;
                c[a[i+1].id]=a[i+1].x-a[i].x;
            }
        }
    top=0;
    fo(i,1,n)
        if (!bz[i]){
            j=i;
            while (j){
                id[++top]=j;
                j=right[j];
            }
        }
    fo(i,0,n)
        fo(j,0,q)
            f[i][j]=-inf;
    f[0][0]=0;
    fo(i,1,q) g[i]=-inf;
    fo(i,1,n)
        fo(j,1,q){
            f[i][j]=max(g[j-1],f[i-1][j-1]+c[id[i]])+p[id[i]];
            if (i>1) g[j]=max(g[j],f[i-1][j]);
        }
    t=0;
    fo(i,1,n) t=max(t,f[i][q]);
    printf("%d\n",ans+t);
    fclose(stdin);fclose(stdout);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值