神O和神牛

13 篇文章 0 订阅

这是一道状压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");
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值