Luogu 1023 - 稅收與補貼問題 - [數學題]
題目連結:https://www.luogu.org/problemnew/show/P1023
題目背景
每樣商品的價格越低,其銷量就會相應增大。現已知某種商品的成本及其在若干價位上的銷量(產品不會低於成本銷售),並假設相鄰價位間銷量的變化是線性的且在價格高於給定的最高價位後,銷量以某固定數值遞減。(我們假設價格及銷售量都是整數)
對於某些特殊商品,不可能完全由市場去調節其價格。這時候就需要政府以稅收或補貼的方式來控制。(所謂稅收或補貼就是對於每個產品收取或給予生產廠家固定金額的貨幣)
題目描述
你是某家諮詢公司的專案經理,現在你已經知道政府對某種商品的預期價格,以及在各種價位上的銷售情況。要求你確定政府對此商品是應收稅還是補貼的最少金額(也為整數),才能使商家在這樣一種政府預期的價格上,獲取相對其他價位上的最大總利潤。
總利潤 = 單位商品利潤 $\times$ 銷量
單位商品利潤 = 單位商品價格 - 單位商品成本 (- 稅金 or + 補貼)
輸入輸出格式
輸入格式:
輸入的第一行為政府對某種商品的預期價,第二行有兩個整數,第一個整數為商品成本,第二個整數為以成本價銷售時的銷售量,以下若干行每行都有兩個整數,第一個為某價位時的單價,第二個為此時的銷量,以一行 $-1,-1$ 表示所有已知價位及對應的銷量輸入完畢,輸入的最後一行為一個單獨的整數表示在已知的最高單價外每升高一塊錢將減少的銷量。
輸出格式:
輸出有兩種情況:若在政府預期價上能得到最大總利潤,則輸出一個單獨的整數,數的正負表示是補貼還是收稅,數的大小表示補貼或收稅的金額最小值。若有多解,取絕對值最小的輸出。
如在政府預期價上不能得到最大總利潤,則輸出“NO SOLUTION”。
輸入輸出樣例
輸入樣例#1:
31
28 130
30 120
31 110
-1 -1
15
輸出樣例#1:
4
說明
所有數字均小於100000
題解:
首先,不妨計算出從“成本價”到“銷量為零的價格”,所有整數單價以及其對應的銷量。
設成本價為 $p$,目標售價為 $T_{價格}$ 及其對應銷量為 $T_{銷量}$,所有可能的售價為 $P_{價格}$ 及其對應銷量為 $P_{銷量}$,假設補貼(稅收)為 $x$,則有:
$(T_{價格}-p+x)T_{銷量} \ge (P_{價格}-p+x)P_{銷量}$
化為:
$x(T_{銷量} - P_{銷量}) \ge (P_{價格}-p)P_{銷量} - (T_{價格}-p)T_{銷量}$
因此,當 $T_{銷量} - P_{銷量} > 0 $ 時,為 $x \ge [(P_{價格}-p)P_{銷量} - (T_{價格}-p)T_{銷量}] / (T_{銷量} - P_{銷量})$;
當 $T_{銷量} - P_{銷量} < 0 $ 時,為 $x \le [(P_{價格}-p)P_{銷量} - (T_{價格}-p)T_{銷量}] / (T_{銷量} - P_{銷量})$;
這樣一來,所有的不等式聯立起來,就對 $x$ 有一個區間限定 $[L,R]$,根據這個區間限定選擇輸出答案即可。
AC程式碼:
#include<bits/stdc++.h> using namespace std; typedef pair<int,double> P; const double eps=1e-8; vector<P> v; P t; int lst,d; inline bool equ(double a,double b) { return fabs(a-b)<eps; } int main() { cin>>t.first; t.second=0; P tmp; while(cin>>tmp.first>>tmp.second) { if(tmp.first==-1 && tmp.second==-1) break; v.push_back(tmp); } sort(v.begin(),v.end()); lst=v[0].first; for(int i=0,j=1,sz=v.size(); j<sz; i++,j++) { double a=(v[i].second-v[j].second)/(v[i].first-v[j].first); double b=v[i].second-v[i].first*a; for(int x=v[i].first+1;x<v[j].first;x++) v.push_back(make_pair(x,a*x+b)); } sort(v.begin(),v.end()); cin>>d; while(1) { P last=v.back(); if(last.second-d>0) v.push_back(make_pair(last.first+1,last.second-d)); else break; } //for(auto x:v) printf("%d %f\n",x.first,x.second); int pos=lower_bound(v.begin(),v.end(),t)-v.begin(); t=v[pos]; //printf("%d %f\n",t.first,t.second); double L=-1e11,R=1e11; for(auto p:v) { if(p==t) continue; if(t.second>p.second) { L=max(L,((p.first-lst)*p.second-(t.first-lst)*t.second)/(t.second-p.second)); } if(t.second<p.second) { R=min(R,((p.first-lst)*p.second-(t.first-lst)*t.second)/(t.second-p.second)); } } //printf("%f %f\n",L,R); if(L>R) printf("NO SOLUTION\n"); else if(L>0) printf("%.0f\n",ceil(L)); else if(R<0) printf("%.0f\n",floor(R)); else printf("0\n"); }