这是一道状压dp题
dp[i]表示状态为i时最小的时间
那么我们考虑什么时候可以办护照
就是下机和登机的那个时间,那么我们枚举这一次办哪个签证,再找一下他能放的最靠前的位置就可以了
如果我们能够把签证办理时间排序,可以发现最优位置单调不降,那么就可以O(2^n*n)做出这道题了
代码写炸了好几次,还看错了题,实际上也不难
#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
int s,len,t,ans,kind,id;
}a[25];
bool cmps(const node &a,const node &b)
{
return a.s<b.s;
}
bool cmpc(const node &a ,const node &b)
{
return a.id<b.id;
}
int dp[5000005],pre[5000005],inf=0x7fffffff/3,dy[5000005],n,p;
int lowbit(register int x){return x&(-x);}
int main()
{
scanf("%d%d",&n,&p);
register int all=(1<<n)-1;
for(register int i=1;i<=n;i++)
scanf("%d%d%d",&a[i].s,&a[i].len,&a[i].t),a[i].id=i;
for(register int i=1,k=1;i<=22;i++,k*=2) dy[k]=i;
for(register int i=1;i<=all;i++) dp[i]=inf;
dp[0]=1;
sort(a+1,a+n+1,cmps);
for(register int i=0;i<=all;i++)
{
for(register int q=all^i;q;q-=lowbit(q))
{
register int j=dy[lowbit(q)];
register int nxt=i^(1<<(j-1)),getport=dp[i];
for(register int k=1;k<=n;k++)
if(a[k].s+a[k].len>getport)
{
if(a[k].s<=getport)getport=a[k].s+a[k].len;
else if(((i>>(k-1))&1)&&a[k].s<=getport+a[j].t)getport=a[k].s+a[k].len;
else if(a[k].s>getport+a[j].t)break;
}
if(getport+a[j].t>=a[j].s)continue;
if(dp[nxt]>getport+a[j].t)
{
dp[nxt]=getport+a[j].t;
pre[nxt]=i;
}
}
}
if(p==2)
{
for(register int i=0;i<=all;i++)
{
if(dp[i]<inf&&dp[i^all]<inf)
{
printf("YES\n");
register int now=i;
while(now)
{
register int pree=pre[now],op=dy[pree^now];
a[op].kind=1;
a[op].ans=dp[now]-a[op].t;
now=pree;
}
now=i^all;
while(now)
{
register int pree=pre[now],op=dy[pree^now];
a[op].kind=2;
a[op].ans=dp[now]-a[op].t;
now=pree;
}
sort(a+1,a+n+1,cmpc);
for(register int i=1;i<=n;i++)
printf("%d %d\n",a[i].kind,a[i].ans);
return 0;
}
}
printf("NO\n");
}
else
{
if(dp[all]<inf)
{
printf("YES\n");
register int now=all;
while(now)
{
register int pree=pre[now],op=dy[pree^now];
a[op].kind=1;
a[op].ans=dp[now]-a[op].t;
now=pree;
}
sort(a+1,a+n+1,cmpc);
for(register int i=1;i<=n;i++)
printf("%d %d\n",a[i].kind,a[i].ans);
return 0;
}
printf("NO\n");
}
}