HDU 4791 Alice's Print Service

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4791

题面:

Alice's Print Service

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1596    Accepted Submission(s): 380


Problem Description
Alice is providing print service, while the pricing doesn't seem to be reasonable, so people using her print service found some tricks to save money.
For example, the price when printing less than 100 pages is 20 cents per page, but when printing not less than 100 pages, you just need to pay only 10 cents per page. It's easy to figure out that if you want to print 99 pages, the best choice is to print an extra blank page so that the money you need to pay is 100 × 10 cents instead of 99 × 20 cents.
Now given the description of pricing strategy and some queries, your task is to figure out the best ways to complete those queries in order to save money.
 

Input
The first line contains an integer T (≈ 10) which is the number of test cases. Then T cases follow.
Each case contains 3 lines. The first line contains two integers n, m (0 < n, m ≤ 10 5 ). The second line contains 2n integers s 1, p 1 , s 2, p 2 , ..., s n, p n (0=s 1 < s 2 < ... < s n ≤ 10 9 , 10 9 ≥ p 1 ≥ p 2 ≥ ... ≥ p n ≥ 0).. The price when printing no less than s i but less than s i+1 pages is p i cents per page (for i=1..n-1). The price when printing no less than s n pages is p n cents per page. The third line containing m integers q 1 .. q m (0 ≤ q i ≤ 10 9 ) are the queries.
 

Output
For each query q i, you should output the minimum amount of money (in cents) to pay if you want to print q i pages, one output in one line.
 

Sample Input
  
  
1 2 3 0 20 100 10 0 99 100
 

Sample Output
  
  
0 1000 1000
 

Source


题意:

    打印张数越多价格区间越优惠,(也可能相同)。问给定张数为n的时候,打印多少张最省钱,即虽然打印多了,但是单价低了,故整个价格也就自然低了。问最后花多少钱。


解题:

    先预处理出每个区间的最小花费,即它的临界值乘以单价,然后从小到大排个序。对于每组样例,算出它在本身区间内的花费,然后用二分法(直接用了upper_bound函数)找出小于其花费的最后一个位置P。从0开始遍历最小花费数组,直至P。若遍历过程中发现,有合法区间,即该区间对应张数大于本身张数,则停止遍历,输出当前代价。其实这样写,算是水过的吧(700ms),复杂度比较高了。

    比较好的解法是先预处理出每个区间的最优值,然后每输入一个数,只要找到该数属于哪个区间即可。(minn的意义是指该区间范围内打印临界张数的最优值)预处理过程为 minn[i]=min(minn[i],minn[i+1]); minn[i]的初始值为直接在该区间打印临界张数的代价。i从大到小遍历一遍即可。因为i+1的张数大于i的张数,故minn[i]可以直接取,minn[i+1]的值,又因为minn[i]是从大到小求得,minn[i+1]保存的是即是后面所有的最优值。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
#include <cmath>
#include <cstdlib>
#include <set>
#include <algorithm>
#include <string>
#include <iomanip>
#define LL long long
using namespace std;
struct cost
{
	int num;
	long long minn;
}store[100010];
bool cmp(cost a,cost b)
{
	return a.minn<b.minn;
}
LL mini[100010];
int main()
{
	int storep[100010],stores[100010];
	int t,n,m,p,tmp;
	bool flag;
	LL val;
    scanf("%d",&t);
	while(t--)
	{
      scanf("%d%d",&n,&m);
	  for(int i=0;i<n;i++)
	  {
		  scanf("%d%d",&stores[i],&storep[i]);
		  store[i].num=i;
		  store[i].minn=1LL*storep[i]*stores[i];
	  }
	  sort(store,store+n,cmp);
	  for(int i=0;i<n;i++)
	  {
		  mini[i]=store[i].minn;
	  }
	  for(int i=0;i<m;i++)
	  {
		  scanf("%d",&tmp);
		  if(tmp>=stores[n-1])
		  {
			  printf("%lld\n",1LL*tmp*storep[n-1]);
		  }
		  else
		  {
			  flag=false;
			  p=lower_bound(stores,stores+n,tmp)-stores;
			  //cout<<"storep: "<<storep[p]<<endl;
			  val=1LL*storep[p-1]*tmp;
			 // for(int i=0;i<p+1;i++)
				  //cout<<"price: "<<storep[i]<<endl;
			  //cout<<"val: "<<val<<endl;
			  p=upper_bound(mini,mini+n,val)-mini;
			  for(int i=0;i<p;i++)
			  {
				  if(stores[store[i].num]>=tmp)
				  {
					  flag=true;
					  printf("%lld\n",store[i].minn);
					  break;
				  }
			  }
			  if(!flag)
			  {
				  if(stores[store[p].num]>=tmp&&mini[p]<=val)
				  {
					  flag=true;
					  printf("%lld",store[p].minn);
				  }
			  }
			  if(!flag)
			  {
				  printf("%lld\n",val);
			  }
		  }
	  }

	}
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值