hdu 5360 set+贪心 // 优先队列+贪心


http://acm.hdu.edu.cn/showproblem.php?pid=5360


Hiking

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1422    Accepted Submission(s): 744
Special Judge


Problem Description
There are  n  soda conveniently labeled by  1,2,,n . beta, their best friends, wants to invite some soda to go hiking. The  i -th soda will go hiking if the total number of soda that go hiking except him is no less than  li  and no larger than  ri . beta will follow the rules below to invite soda one by one:
1. he selects a soda not invited before;
2. he tells soda the number of soda who agree to go hiking by now;
3. soda will agree or disagree according to the number he hears.

Note: beta will always tell the truth and soda will agree if and only if the number he hears is no less than  li  and no larger than  ri , otherwise he will disagree. Once soda agrees to go hiking he will not regret even if the final total number fails to meet some soda's will.

Help beta design an invitation order that the number of soda who agree to go hiking is maximum.
 

Input
There are multiple test cases. The first line of input contains an integer  T , indicating the number of test cases. For each test case:

The first contains an integer  n   (1n105) , the number of soda. The second line constains  n  integers  l1,l2,,ln . The third line constains  n  integers  r1,r2,,rn (0lirin)
It is guaranteed that the total number of soda in the input doesn't exceed 1000000. The number of test cases in the input doesn't exceed 600.
 

Output
For each test case, output the maximum number of soda. Then in the second line output a permutation of  1,2,,n  denoting the invitation order. If there are multiple solutions, print any of them.
 

Sample Input
  
  
4 8 4 1 3 2 2 1 0 3 5 3 6 4 2 1 7 6 8 3 3 2 0 5 0 3 6 4 5 2 7 7 6 7 6 8 2 2 3 3 3 0 0 2 7 4 3 6 3 2 2 5 8 5 6 5 3 3 1 2 4 6 7 7 6 5 4 3 5
 

Sample Output
  
  
7 1 7 6 5 2 4 3 8 8 4 6 3 1 2 5 8 7 7 3 6 7 1 5 2 8 4 0 1 2 3 4 5 6 7 8


PS: 主要到这道题是special judge 答案不唯一

题目大意:邀请n个人去旅游,给出每个人同意邀请时对人数的限制条件:最小人数和最大人数,一旦接收邀请就不会再退出,求一个序列,在这个序列中可以邀请到最多的人去旅游。


//5360 贪心
//范围在l~r
//输出最大的soda的数量,在第二行输出n个数的排列
//http://blog.csdn.net/cq_pf/article/details/47320545	
//n个人接受邀请的条件是已经接受邀请的人数区间在l[i] , r[i] 
//问怎样设置邀请顺序能使得接受邀请的人数最多
//先对其对从小到大排序
//然后维护一个set,set中存入左边满足条件的所有人,
//然后贪心策略是每次取左边满足条件的右边最小的人
//每次多邀请一个人后删除右边不满足条件的人

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <string>
#include <set>
using namespace std;
//set与multiset讲解
//http://blog.csdn.net/xiajun07061225/article/details/7459206   multiset讲解
//http://blog.csdn.net/cq_pf/article/details/47320545 本题

const int maxn = 1e5 + 5;
struct node {
	int l, r, id;
	bool operator<(const struct node tmp) const
	{
		if (l == tmp.l)
			return r < tmp.r;
		else
			return l < tmp.l;
	}
}p[maxn];

multiset<pair<int, int >>s;
multiset<pair<int, int >>::iterator it;

int vis[maxn];//被选上了标记为1
int a[maxn];//最终答案
int main()
{
	//freopen("in.txt","r",stdin);
	int t;
	int n;
	scanf("%d", &t);
	while (t--)
	{
		memset(vis, 0, sizeof(vis));//选上的要标记,因为后面要把没选上的输出
		scanf("%d", &n);
		for (int i = 1; i <=n; i++)
		{
			scanf("%d", &p[i].l);
			p[i].id = i;
		}
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &p[i].r);
		}
		sort(p + 1, p + 1 + n);
		int ans = 0;//当前已经选出的人数
		int i = 1;
		s.clear();
		while (1)
		{
			while (ans >= p[i].l && i <= n)
			{
				s.insert(make_pair(p[i].r, p[i].id));
				i++;
			}
			if (!s.size()) //当s已经清空完了,弹出大的while
				break;
			while (s.size())
			{
				it = s.begin();
				if (it->first >= ans)
					break;
				s.erase(s.begin());//不符合的都弹出去
			}
			if (s.size()) //把剩下符合的放在a[]中
			{
				it = s.begin();
				a[++ans] = it->second;
				vis[it->second] = 1;
				s.erase(s.begin());
			}
		}
		printf("%d\n", ans);

		for (int i = 1; i <= n; i++) //补满输出的数组
		{
			if (!vis[i])
				a[++ans] = i;
			else
				vis[i] = 0;
		}

		for (int i = 1; i <= ans; i++)
			printf(i == ans ? "%d\n" : "%d ", a[i]);
	}
	//fclose("out.txt", "r", stdout);
	system("pause");
	return 0;
}




优先队列做法:

首先对n个人的最小人数限制由小到大排序,记录当前已经接受邀请的人数,然后将满足最小人数的人加入优先队列,优先队列按照最大人数由小到大排列,每次吐出的都是最大人数限制的最小值,如果当前的人数小于等于最大的人数就可以邀请到,否则就邀请不到。每当接受邀请的人数增加,就循环一次,直到所有人都遍历完。


// hdu 5360 优先队列

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

//http://www.bubuko.com/infodetail-1029311.html  优先队列
const int maxn = 1e5 + 5;
struct node {
	int l, r;
	int id;
	bool operator<(node a)const
	{
		return a.r < r;//按照最大值排列
	}
}p[maxn],q;
priority_queue <node> que;
int vis[maxn];
vector<int>vec;
int cmp(node n1, node n2) //先把结构体按照最小值的顺序排列
{
	return n1.l < n2.l;
}


int main()
{
	//freopen("in.txt", "r", stdin);
	int t, n;
	int num;
	scanf("%d", &t);
	while (t--)
	{
		//把优先队列,标记数组,向量  重置为初始状态
		//一般有这种 t-- 要输入很多组数据的都有这一步。。。。。。。
		while (!que.empty()) que.pop();
		memset(vis, 0, sizeof(vis));
		vec.clear();

		scanf("%d", &n);
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &p[i].l);
			p[i].id = i + 1;
		}
		for (int i = 0; i < n; i++)
		{
			scanf("%d", &p[i].r);
		}
		sort(p, p + n, cmp);

		num = 0;
		int i = 0;
		for (int j = 0; j < n; j++)
		{
			while (i < n) //满足最小人数的先放入优先队列
			{
				if (p[i].l <= num)
				{
					que.push(p[i]);
					i++;
				}
				else break;
			}
			while (!que.empty()) //如果优先队列里的数每个的最大值符合,就把id放到向量里面
			{
				if (que.top().r >= num)
				{
					vec.push_back(que.top().id);
					vis[que.top().id] = 1;
					que.pop();
					num++;
					break;
				}
				else
					que.pop();
			}
		}


		//输出了
		int cnt = 0;
		printf("%d\n", vec.size());
		for (int i = 0; i < vec.size(); i++)
		{
			cnt++;
			printf(cnt == n ? "%d\n" : "%d ", vec[i]);
		}
		for (int i = 1; i <= n; i++)
		{
			if (!vis[i])
			{
				cnt++;
				printf(cnt == n ? "%d\n" : "%d ", i);
			}
		}


	}
	system("pause");
	//freopen("out.txt", "r", stdout);
	return 0;
}










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值