【NOIP2015模拟11.3】备用钥匙

大意

在0~m的时间段里,有N个职员在公司工作,每个人在从 sti 出公司出差,在 eni 回公司,公司有个大门,有K把备用钥匙,门一开始锁着,在公司内部可以开锁上锁,出去了或者要进来时,只有带了备用钥匙的人才能开锁上锁,现在在保证每个人出入无忧的情况下,求上锁最长时间。
在每个单位时间里,至多有1个人经过大门。
N,K<=1000

分析

初看这道题,可能不知道如何决策,只能乱搞。其实每个 sti eni 分割出了很多个时间段,有四种情况
1,如果是两进,那么后一个人有钥匙前一个人进门后就可以锁门。
2,如果是左进右出,那么肯定可以锁门,钥匙都不用。
3,如果是两出,那么前一个人有钥匙就可以锁门。
4,如果是左出右进,只有两人都有钥匙前一个人才能锁门。

2就直接加进答案。
1、3两种情况我们可以设一个val[]来记录一个人拿了钥匙之后,对于1、4两种情况可取的时间段和,这个不会有后效性。
情况4是最关键的,如果没有3的话,前面的就可以一个dp搞定。

转化

把每个人看成点,情况4就可以看作是一个点走到另一个点的无向边,从而获得这条边的价值,即这个时间段。
显然,只会出现链。

回归

重新编号,同一条链的点排在一起,只取一个点x,获得val[x](编号后重新构造的val[]),连续取x+1、x(同一条链)就可以获得额外价值。
这样,转化成了dp,再加上一个简单优化,过了。

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int N=5005;
struct rec
{
    int val,inv;
}b[N*5];
int n,m,k,j,s[N],t[N],f[2005][2005][2],ans,dur,p,i,t1,pd[N],a[N],pp,ex[N],val[N],rd[N],next[N],co[N],first[N],d[N],tt;
bool cmp(rec a,rec b)
{
    return a.val<b.val;
}

int cr(int x,int y,int z)
{
    rd[++tt]=y;
    next[tt]=first[x];
    co[tt]=z;
    first[x]=tt;
    d[y]++;
}
int dfs(int x,int y)
{
    pd[x]=1;
    a[++tt]=x;
    for(int p=first[x];p;p=next[p])
    if (rd[p]!=y)
    {
        ex[rd[p]]=co[p];
        dfs(rd[p],x);
    }
}
int main()
{
    freopen("key.in","r",stdin);
    freopen("key.out","w",stdout);
    scanf("%d%d%d",&n,&m,&p);
    fo(i,1,n)
    {
        scanf("%d%d\n",&s[i],&t[i]);
        b[i*2-1]={s[i],i};
        b[i*2]={t[i],i+n};
    }
    sort(b+1,b+1+2*n,cmp);
    ans=b[1].val+m-b[2*n].val;
    fo(i,1,2*n-1)
    {
        dur=b[i+1].val-b[i].val;
        if ((b[i].inv<=n&&b[i+1].inv<=n)||b[i].inv==b[i+1].inv-n) val[b[i].inv]+=dur;else
        if (b[i].inv>n&&b[i+1].inv<=n) ans+=dur;else
        if (b[i].inv<=n&&b[i+1].inv>n) 
        {
            cr(b[i].inv,b[i+1].inv-n,dur);
            cr(b[i+1].inv-n,b[i].inv,dur);
        }else
        if (b[i].inv>n&&b[i+1].inv>n) val[b[i+1].inv-n]+=dur;
    }
    tt=0;
    fo(i,1,n)
        if ((!pd[i])&&d[i]<2)
            dfs(i,0);
    fo(i,0,n) fo(j,0,k) f[i][j][0]=f[i][j][1]=-(1<<30);
    f[0][0][0]=0;
    fo(i,1,n)
    {
        f[i][0][0]=0;
        fo(j,1,min(i,p))
        {
            f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
            f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+ex[a[i]])+val[a[i]];
        }
    }
    printf("%d",max(f[n][min(n,p)][0],f[n][min(n,p)][1])+ans);
}

思考

这道题打起来很简单,思考上,问题的两个转化需要耐心和技巧。通过这道题可以发现,草稿纸上画个图是件好事!

你可能感兴趣的:(【NOIP2015模拟11.3】备用钥匙)