URAL 2092 Bolero 暴力枚举

就是说,有个人他要买n张票,每张有各自的价格,然后由于他是学生,所以每张它也可以有对应的折扣

然后这个售票的也在做活动,有m种,每种可以表示为你一次买>=x张时,可以给你y的折扣(少付y%)

但是他只能选择一种折扣,然后剩下的单独去买

然后这个题,唉,上来一看数据范围,n和m都是10^5,然后就想贪心(其实是暴力),后来想了想对于m种活动,其实折扣的区间只有[1,100],对于每种折扣相同的,肯定保留最小的那个x就行

,所以其实只要100种折扣

然后就去贪了,然后就WA了,然后还蜜汁证了一下贪心,感觉自己没错(当然是当时证明出错了),然后就卡住了

然后最后其实是暴力枚举,先把所有的票优先按折扣升序排序,折扣相同按价格降序排序

然后枚举优惠活动,对于每一个,它所要求的那个x和给的折扣y,就先看看所有折扣<=y的有多少个,

假如有k个,k>=x的话,肯定就把前x个给用优惠活动买了就行,剩下的单独去买

如果k<x,那就是说想使用这个优惠活动的话,就肯定后面还有些票要使用较低的折扣,但是有可能前面把这个抵消掉,所以这些也有可能是最佳方案,所以也要去算,具体算的话,后面的那些选取哪个来使用较低优惠是没有方法确定的(至少我没有),然后就只能暴力枚举每个在使用优惠活动后所增加的钱,然后排序,取最小的那些就可以了

然后最后就得到答案了

(后来想了一个更暴力的方法,就是对于每种优惠方案,都去找每个的差值(计正负),然后直接排序,选取最前面的那些,然后T了,算了一下复杂度是100*100000*log100000,果然太大了,不过也算一种思路吧

#include <string>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ll long long
#define eps 1e-6
#define maxn 100005
struct Node
{
	int price, discount;
};
bool cmp(Node &a, Node &b)
{
	return (a.discount < b.discount) || (a.discount == b.discount&&a.price > b.price);
}
Node a[maxn];
int b[105];
int n, m;
ll suffix[maxn];
ll sum[maxn];
ll differ[maxn];
int main()
{
	//freopen("input.txt", "r", stdin);
	//freopen("output.txt", "w", stdout);
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; ++i)
	{
		scanf("%d%d", &a[i].price, &a[i].discount);
	}
	int s, k, p;
	ll t = 0;
	for (int i = 0; i < m; ++i)
	{
		scanf("%d%d", &k, &p);
		if (b[p] == 0 || b[p]>k)
			b[p] = k;
	}
	sort(a, a + n, cmp);
	suffix[n] = 0; sum[0] = 0;
	for (int i = n - 1; i >= 0; --i)
	{
		suffix[i] = suffix[i + 1] + a[i].price*(100 - a[i].discount);
	}
	for (int i = 1; i <= n; ++i)//表示[0,i)的价格和
	{
		sum[i] = sum[i - 1] + a[i - 1].price * 100;
	}
	/*for (int i = 0; i <= n; ++i)
		printf("%d ", sum[i]);
	printf("\n");
	for (int i = 0; i <= n; ++i)
		printf("%d ", suffix[i]);
	printf("\n");*/
	ll ans = suffix[0];
	int lastpos = 0;
	for (int i = 1; i <= 100; ++i)
	{
		if (b[i] == 0)
			continue;
		//printf("%d %d\n", i, b[i]);
		for (int j = lastpos; j <= n; ++j)
		{
			if (j == n || a[j].discount>i)
			{
				if (j >= b[i])
				{
					s = j;
					t = sum[s] - sum[s] / 100 * i + suffix[s];
					if ( t < ans)
					{
						ans = t;
					}
					//printf("add %lld %lld\n", sum[s] - sum[s] / 100 * i, suffix[s]);
					//printf("1discount %d num %d sum %lld\n", i, j, t);
				}
				else
				{
					s = b[i];
					for (int k = j; k < n; ++k)
					{
						differ[k - j] = a[k].price*(a[k].discount - i);
					}
					/*for (int k = 0; k < n - j; ++k)
						printf("%lld ", differ[k]);
					printf("\n");*/
					sort(differ, differ + n - j);
					t = 0;
					for (int k = 0; k < s - j; ++k)
						t += differ[k];
					//printf("add %lld %lld %lld\n", sum[j] - sum[j] / 100 * i, suffix[j], t);
					t = sum[j] - sum[j] / 100 * i + suffix[j] + t;
					if ( t < ans)
					{
						ans = t;
					}
					//printf("2discount %d num %d sum %lld\n", i, j, t);
				}
				lastpos = j;
				break;
			}
		}
	}
	printf("%f\n", ans*0.01);
	//while (1);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值