重点队列题题解

目录

合并果子:

题面精简:

思路:

代码:

田忌赛马:

题面精简:

思路: 

代码:

丑数:

题面精简:

思路:

代码:


合并果子:

来自bzoj没权限可以点这里

题面精简:

给出一个长为n的序列,合并a和b需要a+b点能量,求和并成一个值需要多少能量?

n <= 10000,ai <= 20000

time:10ms

memory:128MiB

思路:

很简单的题,只要每次都选最小的两个数合并,就能实现最优解。

但是,题目数据过大,时限只有10ms,暴力和dp都用不上。

我们可以开一个优先队列,所有数字都从大到小排列。存数时存入负数,这样队头元素永远是最小的那个。

然后的操作就非常无脑了。

代码:

#include<iostream>
#include<queue>
using namespace std;
priority_queue <int> fruit;
int num,ans,sum;
int readint()
{
	char ch = getchar();
	int value = 0,sign = 1;
	while(ch < '0' || ch > '9')
	{
		if(ch == '-') sign = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') value = value * 10 + (int)(ch) - '0',ch = getchar();
	return value * sign;
}
void writeint(int value)
{
	if(value < 0) putchar('-'),value *= -1;
	if(value > 9) writeint(value / 10);
	putchar(value % 10 + '0');
}
int main()
{
	num = readint();
	for(int i = 1;i <= num;i++) fruit.push(-readint());
	for(int i = 1;i < num;i++)
	{
		sum = fruit.top();
		ans -= fruit.top();
		fruit.pop();
		sum += fruit.top();
		ans -= fruit.top();
		fruit.pop();
		fruit.push(sum);
	}
	writeint(ans);
}

田忌赛马:

来自bzoj没权限可以点这里

题面精简:

给出多组的n,以及n个我方速度和n个敌方速度,要求胜场和败场的差越大越好,结果乘以200

n <= 2000

time:1000ms

memory:256MiB

思路: 

这题很经典,n匹马的比赛非常难算,用dp实现非常困难。

如果用队列的话,有一种很好理解的思路。

开两个数组,代表田忌的马和齐王的马,设置两个头节点和两个尾节点,然后把两个数组排序。

由此,头结点代表最慢的马,尾节点代表最快的马。

如果尾结点1代表的马比尾结点2代表的马要快,就解决掉它,这样才能让快马派上快的用途。

如果不足的话,就看头结点1的马是否可以击败头结点2的马,如果可以,就解决掉他,这样慢马也有了贡献。

调整节点位置后,继续执行直到头结点超过尾结点。

再不然,就要考虑特殊情况,如果头结点1的马不能和尾结点2的马打平,就只能拿最慢的马当炮灰了,这没办法啊。

三个if语句就能轻而易举得到答案。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
int read()
{
	int ans = 0,var = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9')
	{
		if(ch == '-') var = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') ans *= 10,ans += (int)(ch) - '0',ch = getchar();
	return ans * var;
}
void writeint(int output)
{
	if(output < 0) putchar('-'),output *= -1;
	if(output > 9) writeint(output / 10);
	putchar(output % 10 + '0');
}
int horse,tj[2003],qw[2003],head_tj,head_qw,tail_tj,tail_qw,win;
int main()
{
	while(horse = read())
	{
		win = 0;
		head_tj = 1,head_qw = 1;
		tail_tj = horse,tail_qw = horse;
		if(!horse) return 0;
		for(int i = 1;i <= horse;i++) tj[i] = read();
		for(int i = 1;i <= horse;i++) qw[i] = read();
		sort(tj + 1,tj + 1 + horse);
		sort(qw + 1,qw + 1 + horse);
		while(horse--)
		{
			if(tj[tail_tj] > qw[tail_qw])
			{
				tail_tj--;
				tail_qw--;
				win++;
			}
			else if(tj[head_tj] > qw[head_qw])
			{
				head_tj++;
				head_qw++;
				win++;
			}
			else if(tj[head_tj] < qw[tail_qw])
			{
				head_tj++;
				tail_qw--;
				win--;
			}
		}
		writeint(win * 200);
		putchar('\n');
	}
}

丑数:

来自bzoj没权限可以点这里

题面精简:

给出一个有k个元素的集合,求出第n个所有因数都属于这个集合的数。

k <= 100;

n <= 100000

time:5000ms

memory:256MiB

通常,限制超过正常值的题目,都十分的变态,但这道题真的简单,更难的我都不会做,有兴趣的可以一试。

思路:

与其被动去寻找丑数,不如自己生成。

为了让丑数序列升序排列,我们需要一些黑科技。

设置两个数组:value和lable,存放因子的值和目前指向的指针(默认为1),ans数组存放丑数序列(ans[1]默认为1)。

每次找到最小的值(value[i] * ans[lable[i]]),存在ans数组顶。然后找到某个value[i] * ans[lable[i]]和最小值相等,把lable[i]加上1。

再找最小数的过程中,如果遇到某个表达式的值和目前的最小值相等,也把lable[i]加上1。

运行n + 1次后,退出循环,输出ans[n + 1]。

代码:

#include<iostream>
using namespace std;
int read()
{
	int ans = 0,var = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9')
	{
		if(ch == '-') var = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') ans *= 10,ans += (int)(ch) - '0',ch = getchar();
	return ans * var;
}
void write(int output)
{
	if(output < 0) putchar('-'),output *= -1;
	if(output > 9) write(output / 10);
	putchar(output % 10 + '0');
}
int num,x_th,value[102],lable[102],ans[100005],len;
int main()
{
	num = read();
	x_th = read();
	for(int i = 1;i <= num;i++)
	{
		value[i] = read();
		lable[i] = 1;
	}
	ans[++len] = 1;
	while(len <= x_th + 1)
	{
		long long minn = 1000000000000;
		for(int i = 1;i <= num;i++)
		{
			if(ans[lable[i]] * value[i] < minn) minn = ans[lable[i]] * value[i];
			else if(ans[lable[i]] * value[i] == minn) lable[i]++;
		}
		ans[++len] = minn;
		for(int i = 1;i <= num;i++) if(minn == value[i] * ans[lable[i]]) lable[i]++;
	}
	write(ans[x_th + 1]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值