HDU 7087解题思路 优先队列 或者化为二进制后按位相减

https://acm.hdu.edu.cn/showproblem.php?pid=7087

本质

  • 这题本质就是每次都把最大的对象塞到分条里,塞不了就NO,全塞完就YES

如果思路偏了,处处都是荆棘

我之前搞过从分条入手,找离分条最近的最大的对象,但是分条容量是会变的啊!这就导致处理一次分条变动就得重新循环,时间复杂度极大,TEL不在话下

对象是不变的,所以我们从对象入手找最大的分条就行了

算法鉴赏

优先队列

开俩优先队列,数值越大优先级越高,队列1装对象,队列2装分条,开循环

每次循环取出1队首(最大的对象)a,然后取出2队首(最大的分条)b

  • 如果a>b,那么最大的对象没有分条可装,退出循环输出No
  • 如果a==b 最大的分条正好能装下最大的对象,那么这俩都出队,继续循环
  • 如果a<b,那么a出队,b出队,b-a入队2

临界条件

  • 若循环过后队列1为空,队列2无论是否为空,都说明对象都装完了,输出Yes
  • 若循环过后队列1非空,但队列2为空,说明分条不够用,输出No
  • 若循环过后队列1非空,队列2非空但是退出了,说明是因为a>b退出的,输出No

由此得出,循环结束条件是二者有一为空,然后判断

若1空,输出Yes

否则输出No 

简单明了

易错点:如果你是在取出队首的时候同时出队了,请在a>b后让a入队1

AC代码

很短,忽略快读的话三十行

#include<cstdio>
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
inline int read() { 快读,可忽略
	int s=0,w=1;
	char c=getchar();
	while(c<'0' || c>'9') {
		if(c=='-') w*=-1;
		c=getchar();
	}
	while(c>='0' && c<='9') {
		s=(s<<3)+(s<<1)+c-'0';
		c=getchar();
	}
	return s*w;
}
int main(){
	int T=read();
	while(T--){
		int n=read(),m=read();
		priority_queue<int,vector<int>,less<int> > p1,p2;
		for(int i=1;i<=n;i++){//对象 
			int t=read();
			p1.push(t);
		}
		for(int i=1;i<=m;i++){//分条 
			int t=read();
			p2.push(t);
		}
		while(!p1.empty() && !p2.empty()){
			int a=p1.top(),b=p2.top();p1.pop(),p2.pop();
			if(a>b)	{
				p1.push(a);break;	
			}
			else if(a==b) continue;
			else if(a<b) p2.push(b-a);
		}
		if(p1.empty())	printf("Yes\n");
		else printf("No\n"); 
	}
	return 0;
}

化为二进制后按位相减

这是一位AC的同学提供的思想,初见这骚操作我惊为天人

遇到2的n次幂都可以往这边想一下

都是整数,用数组模拟二进制减法

减去不大于它的最大的对象,那就先找位数相同的,这样搜索起来比较快

样例2解析

对象2 2 4 8

分条3 13

对象转化为10 10 100 1000

分条转化为11 1101

1101-1000=101

101-100=1

11-10=1

对象还剩下10 分条还剩下1 1 输出No

样例1解析

对象2 2 4 8

分条4 12

对象10 10 100 1000

分条100 1100

1100-1000=100

100-100=0

100-010=010

010-010=0 

剩余对象为空 输出Yes

再看一组Debug

2 2 8 8

8 12

对象10 10 1000 1000

分条1000 1100

1100-1000=100

1000-1000=0

100-10=10

100-10=10

输出Yes

代码略 

对于HDU4546问题,还可以使用优先队列(Priority Queue)来解决。以下是使用优先队列的解法思路: 1. 首先,将数组a进行排序,以便后续处理。 2. 创建一个优先队列(最小堆),用于存储组合之和的候选值。 3. 初始化优先队列,将初始情况(即前0个数的组合之和)加入队列。 4. 开始从1到n遍历数组a的元素,对于每个元素a[i],将当前队列中的所有候选值取出,分别加上a[i],然后再将加和的结果作为新的候选值加入队列。 5. 重复步骤4直到遍历完所有元素。 6. 当队列的大小超过k时,将队列中的最小值弹出。 7. 最后,队列中的所有候选值之和即为前k小的组合之和。 以下是使用优先队列解决HDU4546问题的代码示例: ```cpp #include <iostream> #include <vector> #include <queue> #include <functional> using namespace std; int main() { int n, k; cin >> n >> k; vector<int> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } sort(a.begin(), a.end()); // 对数组a进行排序 priority_queue<long long, vector<long long>, greater<long long>> pq; // 最小堆 pq.push(0); // 初始情况,前0个数的组合之和为0 for (int i = 0; i < n; i++) { long long num = pq.top(); // 取出当前队列中的最小值 pq.pop(); for (int j = i + 1; j <= n; j++) { pq.push(num + a[i]); // 将所有加和结果作为新的候选值加入队列 num += a[i]; } if (pq.size() > k) { pq.pop(); // 当队列大小超过k时,弹出最小值 } } long long sum = 0; while (!pq.empty()) { sum += pq.top(); // 求队列中所有候选值之和 pq.pop(); } cout << sum << endl; return 0; } ``` 使用优先队列的方法可以有效地找到前k小的组合之和,时间复杂度为O(nklog(k))。希望这个解法对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值