【xdoj难题集】1039: 饭桌上的游戏

的连接

2014年的压轴题,又是模拟(话说为什么最近做了这么多模拟)。总之又是被无数细节给坑了,不过最后结果不错,我这个算法还是挺快的。

还是先说说想法,一看到这题的范围,觉得应该不会超时,比较烦的是标记的这个问题,因为如果每轮都要把标记的人算一次的话怕会超时,不过在看到了饭量最大为5之后我打消了这个顾虑,不过我还是没有使用常规的方法(导致因为一个细节错了不少次)。我的想法是因为标记之后死亡的时间是固定的(当然最后发现不尽然),所以只要用一个优先队列储存死亡时间和死亡标号即可,因为我的处决机制使得我不拍鞭尸(重复处决),所以即使重复标记也不怕,然后其他的吃饭就都用一个chek函数搞定就行了。

具体实现的时候被各种细节问题给坑了
1首先构造一组数组列表,把该存的东西都存了,至于两种属性我单开了两个pair类型的set进行储存,这样消除也比较简单
2然后是卡片的问题,这一步出了一个问题,就是我这么每轮输入一个的话有可能会因为提前结束而导致输入的内容不足而影响接下来的结果,所以我修修补补了一下(其实事先用一组数组储存更简单,就是浪费一小点空间)。

3接下来是一个重点,我们要根据选择的卡片进行行动,首先如果要吃饭的就把相应的编号放进chek里处理即可,如果现在的编号被干掉了就把编号给前一个,这样可以保证所有在这个环里的人都是活着的而且每次向右都是正确的下一个。处理完chek我们处理因为诅咒而需要被处决的人,首先进行循环把这些人拉出来一个个干掉,不过这里遇到了困扰了我很久的一个问题,就是如果这一步是被标记的就不会有人吃饭,诅咒的也不需要,其实就相当于被续了一秒,不过只要加入一个小机制就可以了,首先我们用一个变量biao记录标记的次数,这样每次被标记biao就加一,然后在判断是否死亡的时候还要加入biao的数量,因为有一些可能是后来加入的所以只要让死亡时间的部分减去之前(包括自己)标记的数量这样到时候判断加上的就是后来标记的数量了。

最后贴代码

# include <stdio.h>
# include <algorithm>
# include <set>
# include <queue>

using namespace std;

struct man
{
	int ea, y, z;
	int lct, rct;
};

typedef pair<int , int> P;

const int MAX_N = 1005;

bool die[MAX_N];
int T, N, M, now, num;
man stu[MAX_N];
set<P> Y, Z;

void det(int n)
{
	if(die[n])
		return;
	
	num--;
	Y.erase(P(stu[n].y , n));
	Z.erase(P(stu[n].z , n));
	
	stu[stu[n].lct].rct = stu[n].rct;
	stu[stu[n].rct].lct = stu[n].lct;
	
		
	die[n] = true;
	if(n == now)
		now = stu[n].lct; 	
}

inline void chek(int n)
{
	stu[n].ea--;
	
	if(stu[n].ea <= 0)
		det(n);
}

void solve()
{
	fill(die , die + N , 0);
	
	now = 0;
	num = N;
	int b = 0;
	priority_queue<P , vector<P> , greater<P> > que;
	
	int i;
	if(N <= 1)
	{
		for(i = 0 ; i < M ; i++)
			scanf("%d", &now);
		
		printf("%d\n", N);
		return;
	}
	
	
	for(i = 0 ; i < M ; i++)
	{
		int c;
		scanf("%d", &c);

		if(c == 1)
			chek((*Y.begin()).second);
		else if(c == 2)
			chek((*Z.begin()).second);
		else if(c == 3)
		{
			b++;
			que.push(P(i + stu[now].ea - b , now));
		}
		else if(c == 4)
			chek(stu[now].lct);
		else if(c == 5)
			chek(stu[now].rct);
		else 
			chek(now);
			
		while(!que.empty())
		{
			P ty = que.top();
			if(ty.first + b > i)
				break;
				
			que.pop();
			det(ty.second);
		}
		
		int j;
		if(num == 1)
		{
			for(j = i + 1 ; j < M ; j++)
				scanf("%d", &c);
			printf("%d\n", now + 1);
	
			return;
		}
		else if(num == 0)
		{
			for(j = i + 1 ; j < M ; j++)
				scanf("%d", &c);
			puts("0");
			
			return;
		}
		
		now = stu[now].rct;
	}
	
	puts("-1");
}

int main()
{
	scanf("%d", &T);
	
	while(T--)
	{
		Y.clear();
		Z.clear();
		
		scanf("%d %d", &N, &M);
		
		int i, ny, nz;
		for(i = 0 ; i < N ; i++)
			scanf("%d", &stu[i].ea);
			
		for(i = 0 ; i < N ; i++)
			scanf("%d", &stu[i].y);
			
		for(i = 0 ; i < N ; i++)
			scanf("%d", &stu[i].z);
		
		for(i = 0 ; i < N ; i++)
		{
			stu[i].lct = i - 1;
			stu[i].rct = i + 1;
			Y.insert(P(stu[i].y , i));
			Z.insert(P(stu[i].z , i));
		}
		
		stu[0].lct = N - 1;
		stu[N - 1].rct = 0;
		
		solve();
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值