QDU Julyc和CerberuX去吃饭后传(贪心、思维)

Julyc和CerberuX去吃饭后传

发布时间: 2017年6月18日 21:39   最后更新: 2017年6月18日 21:43   时间限制: 1000ms   内存限制: 128M   SPJ

上次Julyc和CerberuX因为去吃饭打的不可开交,而一般人都不会想和CerberuX打架,Julyc也不例外。这次他们去了一条小吃街,小吃街里有n种小吃,Julyc和CerberuX口味不同,对于每一种小吃,每个人都有着各自的对这种小吃的喜爱值,虽然Julyc非常想独自行动,但是每次独自行动后CerberuX都会走丢(即使学校门口的小吃街),所以Julyc和CerberuX必须一起行动,也就是说他们要选择相同的小吃。先约定一个人“吃得好”是指他吃到的小吃的喜爱值(他对这种小吃的喜爱值)累加之和大于没吃到的那些小吃的喜爱值(他对这种小吃的喜爱值)之和。然而他们的饭量有限,并且也不想吃的太少,所以他们必然会吃(n/2)+1(注意是整除)种小吃(每种小吃的份量是相同的,所以不考虑份量问题)。为了不使他俩打起来,必须要让他俩都“吃得好”,现在给你n,给你每个人分别对这n种小吃的喜爱值,请你帮助他们选择(n/2)+1(注意是整除)种合适的小吃,使得Julyc和CerberuX都可以“吃的好”。

第一行输入测试用例的个数T(0<T<=10)。
对于每一个测试用例输入三行,第一行一个正整数n(1<=n<=1e5)代表有n种小吃。
第二行输入n个正整数a1...an(1<=ai<=1e9),ai代表Julyc对第i种小吃的喜爱值。
第三行输入n个正整数b1...bn(1<=bi<=1e9),bi代表CerberuX对小吃的喜爱值。

对于每一个测试用例输出两行,第一行一个正整数k,代表选择了k种小吃。
第二行k个正整数c1...ck(1<=ci<=n),代表选择了哪些小吃,并请按照从小到大的顺序输出。(ci指第ci种小吃)

  复制
1
5
8 7 4 8 3
4 2 5 3 7
3
1 4 5
  复制
1
2
1 2
3 4
2
1 2

思路:

由于必吃n/2+1种,即如果只满足一个人"吃的好"是必定能实现的,就保证了下面的贪心是可行的。首先根据两人的喜爱值排两个序,然后很容易会想到交叉地区构造两个喜爱值的和是可行的,但是何时转移?:哪一方的未得到的喜爱值与总喜爱值的比值较大,则根据有序序列去构造它。

服务器不稳定,T了一发让我以为写一个复杂度较高的函数却不用它会导致超时?否,后来交就过了= =。


代码:

#include <algorithm>
#include <iostream>
#include <string.h>
#include <cstdio>
#define LL long long
using namespace std;
const int maxn = 1e5+5;
// const double eps = 1e-14;
struct node
{
	int id, val1, val2;
} a[maxn], b[maxn];
int vis[maxn], ans[maxn];
int cmp1(node x, node y)
{
	if(x.val1 == y.val1) return x.val2 > y.val2;
	return x.val1 > y.val1;
}
int cmp2(node x, node y)
{
	if(x.val2 == y.val2) return x.val1 > y.val1;
	return x.val2 > y.val2;
}
/*
double work(LL a, LL b)
{
	double mid, l = 0, r = b;
	while(r-l >= eps)
	{
		mid = (r+l)/2;
		if(mid*b > a) r = mid;
		else l = mid;
	}
	return l;
}
*/
int jg(LL a1, LL b1, LL a2, LL b2)
{
	return (b1-a1)*1.0/b1 > (b2-a2)*1.0/b2;
	//return work((b1-a1)*1.0, b1) > work((b2-a2)*1.0, b2);
}
int main()
{
	int t, n, cnt;
	LL sum1, sum2, now1, now2;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d", &n); cnt = n/2+1;
		sum1 = sum2 = 0;
		for(int i = 1; i <= n; ++i)
		{
			scanf("%d", &a[i].val1);
			sum1 += a[i].val1;
			b[i].val1 = a[i].val1;
			a[i].id = b[i].id = i;
		}
		for(int i = 1; i <= n; ++i)
		{
			scanf("%d", &a[i].val2);
			sum2 += a[i].val2;
			b[i].val2 = a[i].val2;
		}
		sort(a+1, a+n+1, cmp1);
		sort(b+1, b+n+1, cmp2);
		now1 = now2 = 0;
		memset(vis, 0, sizeof vis);
		int i = 1, j = 1;
		for(int k = 1; k <= cnt; ++k)
		{
			if(jg(now1, sum1, now2, sum2))
			{
				for(; i <= n; ++i)
					if(!vis[a[i].id]) break;
				vis[a[i].id] = 1;
				now1 += a[i].val1;
				now2 += a[i].val2;
				ans[k] = a[i].id;
				++i;
			}
			else
			{
				for(; j <= n; ++j)
					if(!vis[b[j].id]) break;
				vis[b[j].id] = 1;
				now1 += b[j].val1;
				now2 += b[j].val2;
				ans[k] = b[j].id;
				++j;
			}
		}
		sort(ans+1, ans+cnt+1);
		printf("%d\n", cnt);
		for(int i = 1; i <= cnt; ++i)
			printf("%d%c", ans[i], i==cnt?'\n':' ');
	}
	return 0;
}


继续加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值