【NOIP模拟】备用钥匙

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

这种二元组已经十分熟悉了。
很容易想到枚举区间。
首先把所有的时间放在坐标轴上,那么现在对于当前这个区间关门,谁要拿钥匙就要了方案了,总共分四种情况,这段区间的两个端点:左进右进(右边的拿钥匙),左进右出(中间可以直接锁门,直接统计答案),左出右进(两个人都要拿钥匙),左出右出(左边的拿钥匙)。
我们发现有一种情况是两个都要拿钥匙的,那么就是产生了依赖性,对于这种情况,很容易想到连边,把第三种情况下左边的端点到右边的端点连边,因为要取了这一条边,才可以得到当前锁门的贡献,所以用一个v[i]来表示,这个点和他上一个点都拿钥匙对答案的贡献。
因为第二种情况可以直接处理,不用管它。
那么第一种可第四种情况,都是单个点取钥匙的,没有依赖性,所以用一个c[i]表示当前这个点拿了钥匙对答案的贡献。
因为连过了边,就会形成一条条的链,链与链之间是没有联系的,因为不连起来就说明不存在依赖性,那么可以把这些链或者点随意的压入一个数组里面。
那么现在问题就转换了,有n个点,每个点有权值c[i],可以选k个点,相邻的两个点如果都被选了,额外产生v[i]的收入(因为是很多链和点压入的,所以可能为0),要求最大收益为多少。
把问题转化一下是很好的思路
明显可以做一个DP,f[i,j]表示当前做到第i个点并且第i个点必须选,已经用了j把钥匙的最大收益。
很显然,不考虑相邻的依赖性可以这样转移 f[i,j]=max(f[k,j1])+c[i]1ki2 ,然后就是可以由上一个转移过来了 f[i,j]=max(f[i1,j1]+v[i]+c[i]) ,但是发现这样做是 O(n3) 的,这有转化为经典优化了,发现k是1开头的,用一个g[i,j]表示最大的f[k,j]就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=10000;
int i,j,k,l,t,n,m;
long long ans,sum;
int b[maxn],data[maxn],c[maxn],d[maxn],num,v[maxn];
long long  map[maxn],f[2005][2005],g[2005][2005];
bool bz[maxn];
struct node{
    int x,y;
    bool z;
}a[maxn];
bool cmp(node x,node y){return x.x<y.x;}
int main(){
    freopen("key.in","r",stdin);
    freopen("key.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    fo(i,1,n){
        scanf("%d%d",&a[i*2-1].x,&a[i*2].x);
        a[i*2-1].y=i;a[i*2].y=i;
        a[i*2-1].z=1;a[i*2].z=0;
    }
    sort(a+1,a+1+2*n,cmp);
    ans+=a[1].x+m-a[2*n].x;
    fo(i,2,2*n){
        int o=a[i].x-a[i-1].x;
        if(a[i-1].z){
            if(a[i].z){
                c[a[i-1].y]+=o;
            }
            else{
                if(a[i].y==a[i-1].y){
                    c[a[i].y]+=o;
                    continue;
                }
                map[a[i-1].y]=a[i].y;
                bz[a[i].y]=1;
                v[a[i].y]=o;
            }
        }
        else{
            if(a[i].z){
                ans+=o;
            }
            else{
                c[a[i].y]+=o;
            }
        }
    }
    fo(i,1,n){
        if(!bz[i]){
            j=i;
            while(j!=0){
                bz[j]=1;
                d[++num]=j;
                j=map[j];
            }
        }   
    }
    fo(i,1,n){
        fo(j,1,k){
            if(j>1)f[i][j]=max(g[i-2][j-1],f[i-1][j-1]+v[d[i]]);
            f[i][j]+=c[d[i]];
            g[i][j]=max(g[i-1][j],f[i][j]);
            sum=max(sum,f[i][j]);
        }
    }
    printf("%lld\n",sum+ans);
}

你可能感兴趣的:(noip,DP,JOI)