【JZOJ4301】备用钥匙

Description

你知道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社的社长很重视时间效率,因此每个员工在离开或回到公司的时刻以外,不允许开锁或者上锁。
出于安全的考虑,社长希望上锁的时间越长越好。现在他将员工出入公司的信息和准备交给员工的钥匙数量告诉了你,请你求出在能使所有员工回到公司的时候都能进入公司的大门的前提下,上锁的时间最长是多少。

Solution

由于每个时刻不会有两个人进出,所以我们把进出当成一个个点,抽象在一个时间轴上,问题转化成取得时间总和最多的区间,那么我们可以对相邻的两个点分类讨论:

左出右出:只要左边的人有钥匙就可以取。
左进右进:只要右边的人有钥匙就可以取。
左进右出:不需要钥匙,直接统计入答案。
左出右进:两个人都要有钥匙才可以取。

!这里的相邻的点要满足两个点不属于同一个人。(当两个点是同一个人时,就把这段区间的贡献加进那个人身上)

对于1、2种情况,把这段区间加入到持钥匙那个人上,而对于第4种情况,则用一条边把两个人连起来,边权为这段区间的贡献。

处理完后,我们会有许许多多的连通块,而这些连通块都是一条链的形式。
于是,我们把这些连通块排起来,给他们重新编号。

这样题目就变成了n个点选k个点的最大贡献,且选取一个点和相邻的点有额外贡献。
我们可以设个dp解决:
Fi,j 表示当前要选第i个点,选了k个点的最大贡献。
Fi,j = max(max(F1,j,F2,j,...,Fi2,j),Fi1,j+lasti)
lasti 表示当前点与上一个点同时选的额外贡献)
这里 max(F1,j,F2,j,...,Fi2,j) 可以用一个动态数组来记录。

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 2001
#define ll long long
using namespace std;
int t=0;
struct node{
    int v,x,p;
}a[N];
int n,m,K;
int last[N];
int b[N];
ll f[N][N],g[N];
int p[N];
int q=0;
bool cmp(node x,node y)
{
    return x.v<y.v;
}
bool bz[N];
int c[N];
void dfs(int x)
{
    bz[x]=true;
    if(b[x]) dfs(b[x]);
    c[c[0]--]=x;
}
int main()
{
    freopen("key.in","r",stdin);
    freopen("key.out","w",stdout);
    cin>>n>>m>>K;
    fo(i,1,n)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        t++;
        a[t].v=x,a[t].x=1,a[t].p=i;
        t++;
        a[t].v=y,a[t].x=0,a[t].p=i;
    }//
    sort(a+1,a+t+1,cmp);
    ll s=a[1].v+m-a[t].v;
    fo(i,2,t)
    {
        int z=a[i].v-a[i-1].v;
        if(a[i-1].x==0 && a[i].x==0) p[a[i].p]+=z;
        else if(a[i-1].x==1 && a[i].x==1) p[a[i-1].p]+=z;
        else if(a[i-1].x==1 && a[i].x==0)
        {
            if(a[i-1].p==a[i].p)
            {
                p[a[i-1].p]+=z;
                continue;
            }
            last[a[i].p]=z;
            b[a[i-1].p]=a[i].p;
            bz[a[i].p]=true;
        }
        else if(a[i-1].x==0 && a[i].x==1) s+=z;
    }
    c[0]=n;
    fo(i,1,n)
    if(!bz[i]) dfs(i);
    ll ans=0;
    fo(i,1,n)
    fo(j,1,K)
    {
        if(j==1) f[i][j]=max(g[j-1],f[i-1][j-1])+p[c[i]];
        else f[i][j]=max(g[j-1],f[i-1][j-1]+last[c[i]])+p[c[i]];
        g[j]=max(g[j],f[i-1][j]);
    }
    fo(i,1,n) ans=max(ans,f[i][K]);
    cout<<ans+s;
}
posted @ 2016-04-20 20:26  sadstone  阅读(36)  评论(0编辑  收藏  举报