日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HDU6438 Buy and Resell 解题报告(一个有趣的贪心问题的严格证明)

發布時間:2023/12/13 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HDU6438 Buy and Resell 解题报告(一个有趣的贪心问题的严格证明) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在前面

此題是一個很容易想到的貪心題目,但是正確性的證明是非常復雜的。然而,目前網上所有題解并未給出本題貪心算法的任何正確性證明,全部僅停留在描述出一個貪心算法。本著對算法與計算機科學的熱愛(逃),我花了2周時間深入研究了這個問題,并請教了Apass.Jack?大牛,終于在他的幫助下證明了該貪心的正確性。接下來將給出詳細地證明過程。

PS:Apass.Jack提供了整個證明框架(盡管后來被我發現了一處錯誤并重新修正了證明),在此表示感謝!

題目描述

給定$n$($n \le 10^5)$個城市的物品價格,每個城市只能最多購買或賣出一個物品,可以既不買也不賣。當然,若當前手上沒有物品則不能賣出。問從城市1走到城市$n$,途中進行買賣后最多賺多少錢,在此前提下最少進行多少次交易。初始手上沒有任何物品,但有無限的錢。

Sample Input

3 4 1 2 10 9 5 9 5 9 10 5 2 2 1

Sample Output

16 4 5 2 0 0

算法描述

我們先不考慮最小化交易次數,只考慮利潤最大化。本題有一個比較好想的貪心算法:

從左往右遍歷城市,并維護每個城市當前的狀態(買了物品、賣了物品或什么都不做)。對于城市$i$,我們找1到$i-1$中價格最低的$j$且$j$城市未買入(可以賣出),如果$j$城市價格比$i$城市低,則在$j$城市買入并在$i$城市賣出。同時更新$j$城市的買賣狀態:若之前狀態是賣出,則更新為什么都不做;否則更新為買入。

這樣遍歷完1到$n$即可得出答案。實現時,顯然可用優先隊列維護。這樣時間復雜度$O(n\log n)$。

算法正確性證明

基本術語

為表述方便,引入以下記號:

(1)用$p_i$表示城市$i$的價格;

(2)一個策略定義為每個城市的買賣狀態集合,用$s_i$表示。其中,$s_i=0$表示什么都不做;$s_i=1$表示買入;$s_i=-1$表示賣出。

(3)定義$s$的前綴和為$h_s(i)=\sum_{i=1}^n {s_i}$,并補充$h_s(0)=0$;顯然一個方案是合法的,當且僅當對任意$1 \le i \le n$,$h_s(i) \ge 0$。

(4)定義$(i,j)$表示一個升序二元組$(i < j)$;

(5)稱$(i,j)$被策略$s$分開,或稱在策略$s$中$(i,j)$不可能屬于同一次買賣,如果存在$i \le k < j$,使得$h_s(k)=0$。

證明思路

此題直接證明是非常困難的,因為它會動態修改之前的策略。我們考慮引入一個中間步驟,即得出最優解滿足的充要性質,然后證明貪心的解也滿足這個性質,那么貪心就自然是正確的了。

接下來將證明以下幾個主要定理,分別建立最優解的性質以及貪心解的性質。

另外為了簡化證明,以下部分均假設所有$p_i$互不相同。顯然如果$p_i$互不相同時算法正確,那么$p_i$可以相同時算法也必然正確(通過取極限)。

定理1

如果一個策略$s$是最優策略,則$h_s(n)=0$,且對于任意$(i,j)$一定滿足:

(1)若$s_i=s_j=0$或$s_i<s_j$,必有$p_i>p_j$。

(2)若$s_i=s_j=0$或$s_i>s_j$,且$p_i>p_j$,$(i,j)$必然被分開。

證明:$h_s(n)=0$顯然。

(1)若不然,我們將$s_i$加1,將$s_j$減1,顯然對任意$i$有$h_s(i) \ge 0$,因此是合法方案,但利潤更大了,矛盾。

(2)若不然,則對任意$i \le k<j$有$h_s(k)\ge1$。我們令$s_i$減1,$s_j$加1,顯然對任意$i$,$h_s(i)\ge 0$仍成立,因此是合法方案,但利潤更大了,矛盾。

定理2

滿足定理1的策略必然由貪心算法給出。

證明:用數學歸納法,$n=1$顯然成立。

考慮$n$時滿足定理1的一個策略,由于$h_s(n)=0$故必有$s_n=0$或$s_n=-1$。

(1)若$s_n=0$,那么該策略在前$n-1$個城市中滿足定理1策略,已由貪心算法給出。當貪心算法到第$n$個城市時,它一定什么都不做,若不然,必存在$p_j<p_n$且$s_j \le 0$。那么二元組$(j,n)$和定理1(1)矛盾。

(2)若$s_n=-1$。設$k$是$0 \le k<n$中滿足$h_s(k)=0$的最大的$k$,$j$為$k<j\le n$中$s_j \ge 0$且價格最高的城市。

考慮把$s_j$減1而其它不變,這樣我們得到一個新策略,將其記為$w$,那么新的策略$h_w(n-1)=0$且$h_w$恒非負,此時$w_1$到$w_{n-1}$便是前$n-1$個城市的一個合法方案。下證它滿足定理1的條件。

注意一個性質:如果$(i,j)$在$s$中被分開,在$w$中一定被分開,因為$w$只有$j$處比$s$小1,其它值都相同。利用該性質,對于策略$w$,任何不包含$j$的二元組必然滿足定理1的條件;我們只需考慮包含$j$的二元組是否滿足定理1條件即可。

對于定理1(1):

a):若$(i,j)$滿足$w_i=w_j=0$或$w_i<w_j$,必有$s_i<s_j$,由于$s$滿足定理1(1)故$p_i>p_j$;

b):若$(j,i)$滿足$w_j=w_i=0$或$w_j<w_i$,那么$s_i=w_i \ge 0$,由于$j$是$k$之后$s$非負的價格最高者,故$p_j>p_i$。

對于定理1(2):

a):若$(i,j)$滿足$w_i>w_j$或$w_i=w_j=0$,且$p_i>p_j$,那么$s_i=w_i \ge 0$,由于$j$是$k$之后$s$非負的價格最高者,故必有$i \le k$,故$(i,j)$被$s$分開;

b):若$(j,i)$滿足$w_j>w_i$或$w_i=w_j=0$,且$p_j>p_i$,那么$s_j>s_i$,由定理1(2)$(j,i)$在$s$策略中被分開,這與$k$是最大的$h_s(k)=0$的城市矛盾,不會有這種情況。

綜上,$w$是滿足定理1的策略,由歸納知必然是貪心算法前$n-1$個城市的執行結果。最后考慮算法在第$n$個城市的操作。

首先必有$p_j<p_n$,若不然由$s_j \ge 0 >-1=s_n$但$(j,n)$并未在$s$中被分開可導出和定理1(2)的矛盾。而$w_j \le 0$,故算法必然會在$n$城市賣出(因為可以在$j$買入且能增大利潤),下面只需考慮是不是一定買入$j$城市。設算法買入的城市為$t$,且$p_j>p_t$,那么$s_t \le 0$而$s_j \ge 0$,那么$t$不能在$j$之前(否則與定理1(1)矛盾)。同樣$t$也不能在$j$之后(否則由定理1(2)$(j,t)$被$s$分開,與$k$是最大的$h_s(k)=0$的城市矛盾),因此買入的城市$t=j$。因此滿足定理1的策略確實完全由貪心算法給出。證畢。

定理3

滿足定理1的策略是唯一的,從而貪心算法正確。

證明:由于定理1的策略必由貪心算法給出,而貪心算法給出的策略只有一個,故滿足定理1的策略是唯一的。又由定理1,最優策略滿足定理1,故算法是正確的。

最小化交易次數

根據上面定理,當價格互不相同時最優策略是唯一的,但價格可以相同時則不一定,此時才存在最小化交易次數的問題。此時考慮修改算法,當優先隊列中最低價格不止一個時,優先取原本賣出物品的城市,這樣該城市的狀態會被修改成什么都不做。這個正確性很顯然。對于價格相同的物品,它們對于后續城市是完全等價的,因此盡可能的將它們變成不買也不賣必然達到最小交易次數。

綜上,這道題目就完全解決了。

參考代碼

1 #include<cstdio> 2 #include<queue> 3 using namespace std; 4 int p[100001]; 5 struct Node{ 6 int i; 7 bool sell; 8 bool operator < (const Node t)const{ 9 return p[i] != p[t.i] ? p[i] > p[t.i] : !sell && t.sell; 10 } 11 }; 12 int main() 13 { 14 int test, n; 15 scanf("%d", &test); 16 while (test--){ 17 priority_queue<Node> q; 18 scanf("%d", &n); 19 for (int i = 0; i < n; i++) 20 scanf("%d", &p[i]); 21 long long ans = 0; 22 int cnt = 0; 23 q.push({ 0, 0 }); 24 for (int i = 1; i < n; i++){ 25 int j = q.top().i; 26 if (p[j] < p[i]){ 27 bool sell = q.top().sell; 28 q.pop(); 29 ans += p[i] - p[j]; 30 if (sell)q.push(Node{ j, 0 }); 31 else cnt += 2; 32 q.push(Node{ i, 1 }); 33 } 34 else q.push(Node{ i, 0 }); 35 } 36 printf("%lld %d\n", ans, cnt); 37 } 38 }

?

轉載于:https://www.cnblogs.com/zbh2047/p/9736378.html

總結

以上是生活随笔為你收集整理的HDU6438 Buy and Resell 解题报告(一个有趣的贪心问题的严格证明)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。