题目背景
每样商品的价格越低,其销量就会相应增大。现已知某种商品的成本及其在若干价位上的销量(产品不会低于成本销售),并假设相邻价位间销量的变化是线性的且在价格高于给定的最高价位后,销量以某固定数值递减。(我们假设价格及销售量都是整数)
对于某些特殊商品,不可能完全由市场去调节其价格。这时候就需要政府以税收或补贴的方式来控制。(所谓税收或补贴就是对于每个产品收取或给予生产厂家固定金额的货币)
题目描述
你是某家咨询公司的项目经理,现在你已经知道政府对某种商品的预期价格,以及在各种价位上的销售情况。要求你确定政府对此商品是应收税还是补贴的最少金额(也为整数),才能使商家在这样一种政府预期的价格上,获取相对其他价位上的最大总利润。
总利润=单位商品利润 \times× 销量
单位商品利润=单位商品价格 - 单位商品成本 (- 税金 or + 补贴)
输入输出格式
输入格式:
输入的第一行为政府对某种商品的预期价,第二行有两个整数,第一个整数为商品成本,第二个整数为以成本价销售时的销售量,以下若干行每行都有两个整数,第一个为某价位时的单价,第二个为此时的销量,以一行-1−1,-1−1表示所有已知价位及对应的销量输入完毕,输入的最后一行为一个单独的整数表示在已知的最高单价外每升高一块钱将减少的销量。
输出格式:
输出有两种情况:若在政府预期价上能得到最大总利润,则输出一个单独的整数,数的正负表示是补贴还是收税,数的大小表示补贴或收税的金额最小值。若有多解,取绝对值最小的输出。
如在政府预期价上不能得到最大总利润,则输出“NO SOLUTION”。
输入输出样例
输入样例#1: 复制
31 28 130 30 120 31 110 -1 -1 15
输出样例#1: 复制
4
说明
所有数字均小于100000
从样例开始分析。
输入是:
31 28 130 30 120 31 110 -1 -1 15
意思就是政府预期价是31元。成本28元,按成本销售的时候可以买130件产品。
每个卖30元的时候可以卖120个,
每个卖31元(输入的最高价位)的时候可以卖110个,
每个卖32元的时候可以卖:110-15=95个。
每个卖33元的时候可以卖:110-15-15=80个。
每个卖34元的时候可以卖:110-15-15-15=65个。
… 因为“相邻价位之间的销量变化是均匀的”,因此28元卖130个,30元卖120个就可以知道
29元卖125个(平均每元减少的销量是(130-120) div (30-28)=5)
输出是4,我们来解释一下为什么是4。
4代表补贴是4元,所以:
在卖28元的时候,总利润是:(28-28+4)*130=520元,
在卖29元的时候,总利润是:(29-28+4)*125=625元,
在卖30元的时候,总利润是:(30-28+4)*120=720元,
在卖31元的时候,总利润是:(31-28+4)*110=770元,
在卖32元的时候,总利润是:(32-28+4)*95=760元,
… 在卖38元的时候,总利润是:(38-28+4)*5=70元,
显然可能的价位就是28~38了。(不能低于成本,卖39的时候销售量就是负数了)
可以看出,现在卖31元最划算,所以人们都愿意卖31元,这样一来不就达到政府的目的了吗!!
而当补贴是0,1,2,3的时候卖31元并不是最划算的,政府的目的达不到,你当然就没有分啦!
题意清楚了吗?好,下面分析思路。
穷举显然可以,但是没有什么意思,留给大家自己写。下面讲我的另外一种算法,数学味道要浓一些,
希望大家坚持看完。
由于需要N元钱最划算,相当于使N元钱的利润大于等于每种价格的利润。因此可以分别考虑。
设补贴为x,则N元钱的利润是:(p为成本)
(N-p+x)*d[N]=(N-p)*d[N]+x*d[N]
因此N元钱比M元钱划算的时候有:
(N-p)*d[N]+x*d[N]>=(M-p)*d[M]+x*d[M],即:
x(d[N]-d[M])>=M*d[M]-N*d[N]-p*(d[M]-d[N])
这样,要使N元钱比M元钱划算,x必须在区间[k1,k2] (k1,k2根据上面的式子得出)
例如上面的例子:
31元比28元划算时有:
(31-28+x)*110>=(28-28+x)*130
即:330+110x>=130x,故x<=16.5
31元比30元划算时有:
330+110x>=240+120x,故x<=9
31元比32元划算时有:
330+110x>=380+95x,故x>=3.33
… 最后所有式子取交集,就得到了x的范围。要求绝对值最小值还不容易吗? :-P
大家注意我在求出了k1,k2后做的最后的处理。可能有一边或两边无界的情况。
正数和负数的处理也有区别。
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxm = 1e5 + 5;
const int INF = 1e8;
int val[maxm],num[maxm];
int main()
{
int value,m = 1;
int lv,ln,nv,nn,jian,pos = 0;
scanf("%d",&value);
scanf("%d %d",&val[1],&num[1]);
while (true)
{
scanf("%d %d",&nv,&nn);
if (nv==-1 && nn==-1) break;
lv = val[m];
ln = num[m];
while (val[m]<nv)
{
m++;
val[m] = val[m-1] + 1;
num[m] = num[m-1] - (ln-nn)/(nv-lv);
}
}
scanf("%d",&jian);
while (num[m]-jian>0)
{
m++;
val[m] = val[m-1] + 1;
num[m] = num[m-1] - jian;
}
for (int i=1;i<=m;i++)
if (val[i]==value)
{
pos = i;
break;
}
if (pos==0)
{
printf("NO SOLUTION\n");
return 0;
}
double limtmax = 1e8,limtmin = -1e8;
for (int i=1;i<pos;i++)
{
double limt = (double)((val[pos]-val[1])*num[pos]-(val[i]-val[1])*num[i])/(double)(num[i]-num[pos]);
limtmax = min(limtmax,limt);
}
for (int i=pos+1;i<=m;i++)
{
double limt = (double)((val[i]-val[1])*num[i]-(val[pos]-val[1])*num[pos])/(double)(num[pos]-num[i]);
limtmin = max(limtmin,limt);
}
if (limtmin>limtmax)
printf("NO SOLUTION\n");
else if (limtmax<0) printf("%.0lf\n",floor(limtmax));// 最小值向上取整
else if (limtmin>0) printf("%.0lf\n",ceil(limtmin));// 最大值向下取整
else printf("0\n");
return 0;
}