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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/249371.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/249371.shtml
英文地址,請注明出處:http://en.pswp.cn/news/249371.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Webpack不生成index.html

沒有導出你的最后2個插件&#xff0c;并且沒有指定html文件名dist&#xff0c;因為HtmlWebpackPlugin應該生成相對于path&#xff0c;下面是固定配置&#xff1a; var path require(path)var webpack require(webpack)var HtmlWebpackPlugin require(html-webpack-plugin);m…

CSS3筆記之定位篇(一)relative

知識點1&#xff1a;relative和absolute relative: 相對自身&#xff0c;并會限制內部absolute元素層疊 absolute: 相對容器&#xff0c;并受到父類容器relative的影響&#xff0c;比如&#xff1a;overflow:hidden/scroll fixed: 不受relative限制&#xff0c;只受z-index的…

洛谷P3066 [USACO12DEC]逃跑的BarnRunning Away From…

題面鏈接 一句話題意&#xff1a;給出以1號點為根的一棵有根樹&#xff0c;問每個點的子樹中與它距離小于等于l的點有多少個。 我&#xff1a;似乎并不好做啊。。。看了題解后大霧。。。 sol&#xff1a;考慮樹上差分&#xff0c;對于一個點&#xff0c;在他那個位置&#xff0…

vue使用webPack打包發布后頁面顯示空白

今天筆者將打包后&#xff0c;進行訪問&#xff0c;訪問到index.html&#xff0c;但是出現的是空白頁。 打包命令&#xff1a;npm run build&#xff0c;打包后的文件如下&#xff1a; 這是因為index.html中引入的css ,js 的路徑不對:如下圖 這個是因為webpack打包的時候引入…

第一次實驗報告

c程序實驗報告 姓名&#xff1a;黃志乾 實驗地點&#xff1a;教學樓514教室 實驗時間&#xff1a;3月19日實驗項目: 1、字符與ASCII碼 2、運算符與表達式的應用 3、順序結構應用程序 4、數學函數的算法描述 5、雞兔同籠的算法描述 6、確定坐標的算法描述…

Mac下Idea安裝Git報錯Xcrun問題的解決

使用過IDEA的小伙伴都知道&#xff0c;它和我們之前用過的Eclipse一樣強大&#xff0c;或者比他更強大。當它配合的Mac使用時&#xff0c;就會變得更得心應手&#xff0c;少去很多環境配置的環節。其中最典型的就是Git 由于Mac自帶就安裝了git, 大家可以通過終端輸入命令“git…

關于Django路由層簡單筆記

Django—路由層 URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL與要為該URL調用的視圖函數之間的映射表&#xff1b;你就是以這種方式告訴Django&#xff0c;對于客戶端發來的某個URL調用哪一段邏輯代碼對應執行。 1&#xff0c;簡單的路由配置 from django.urls…

hdu 5183

hdu 5183(Hash處理區間問題) 題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid5183 題意:給出一個n個元素的數組,現在要求判斷 a1-a2a3-a4...../-an 中是否存在某個某個區間使得 ai-ai1ai2...(-1)j-iaj k?? 這個題要利用Hash就可以實現幾乎在 O(n) 的時間內實現查找判斷…

vue-cli,webpack安裝

第一步應該下載node.js這是安裝vue-cli的基礎工具。官網下載快捷安全可&#xff1a;https://nodejs.org/en/ 第二步打開命令面板找到你要安裝的位置 第三步就是安裝全局vue-cli 命令操作 npm intatll -g vue-cli 安裝完畢之后 可以檢查安裝版本即 vue -V 如下圖 這還不算完&…

CSS3筆記之定位篇(二)z-index

知識點1&#xff1a;z-index基礎 z-index&#xff1a;auto; 默認值 z-index: <integer> 整數 z-index: inherit 繼承 不考慮css3 還有定位元素的z-index才有作用 知識點2&#xff1a;z-index與定位元素 無嵌套&#xff1a;后來居上&#xff0c;哪個大哪個上 //在沒有…

JSP頁面傳值出現中文亂碼的問題

在接收值的jsp頁面代碼的body里添加&#xff1a; <%request.setCharacterEncoding("utf-8"); %> //這里是設置utf-8為jsp頁面的中文編碼方式 jsp頁面之間傳值&#xff1a; 發送信息的jsp腳本&#xff1a; session.setAttribute("user",rs.getString…

【我所認知的BIOS】— uEFI AHCI Driver(8) — Pci.Read()

【我所認知的BIOS】—> uEFI AHCI Driver(8) — Pci.Read()LightSeed6/19/2014社會一直在變。不曉得是不是社會變的太苦開&#xff0c;而我沒變所以我反而顯得單純了。辦一個居住證。幾年前辦的以為最終能夠一勞永逸的&#xff0c;后來續辦的是發現確實不難了。尼瑪&#xf…

springboot項目集成vue

vue的項目目錄如下&#xff1a; vue項目打包 首先進入項目目錄&#xff1a;cd 項目名 然后執行打包命令&#xff1a;npm run build隨后我們的項目中會多出一個dist文件夾&#xff1a;如下圖 然后將dist文件夾中的所有內容放到eclipse中的src/main/resources/static文件夾里面…

Vue項目啟動webpack報錯Module build failed: Error: No PostCSS Config found in......

自己寫的公司項目&#xff0c;今天需要提交到公司版本庫&#xff0c;可是在本地啟動正常的項目&#xff0c;拷貝到git文件目錄下突然報錯Module build failed: Error: No PostCSS Config found in......&#xff0c;源文件都沒有改動過&#xff01; 然后自己各種百度&#xff…

2.1對 特征歸一化 的一些理解

特征歸一化有很多不同的叫法&#xff0c;比如&#xff1a;特征縮放&#xff0c;Feature Normalization&#xff0c;Feature Scaling 數據標準化&#xff08;歸一化&#xff09;處理是數據挖掘的一項基礎工作&#xff0c;不同評價指標往往具有不同的量綱和量綱單位&#xff0c;這…

逆向工程生成的Mapper.xml以及*Example.java詳解

逆向工程生成的接口中的方法詳解 在我上一篇的博客中講解了如何使用Mybayis逆向工程針對單表自動生成mapper.java、mapper.xml、實體類&#xff0c;今天我們先針對mapper.java接口中的部分方法進行測試&#xff0c;以了解其作用。 先看表結構。。。 從下圖可以看到MBG根據數據表…

SpringBoot之靜態資源訪問

SpringBoot之靜態資源訪問 1.springboot訪問靜態資源的幾種方式 (1)在src/main/resources/目錄下創建 static文件夾 (2)在src/main/resources/目錄下創建 resources文件夾 (3)在src/main/resources/目錄下創建 public文件夾 (4)在src/main/resources/目錄下創建 META-INF/resou…

幾何

題目大意定義一個$S-$四面體表示六條邊由$S$根不同的木棍組成&#xff0c;定義一種染色方法合法當且僅當至少有$S$根木棍被染色且與每個頂點相鄰的三根木棍中至多有一根被染色&#xff0c;求有$N$個$S1,2...N$四面體&#xff0c;求至少染$K$個的方案數。 題解 單獨考慮$S1$四面…

VUE的element-ui的使用

我們在自己的網站當中有的時候會用到element-ui的組建 1.如何安裝element-ui的組件 在命令行工具當中輸入cnpm i element-ui -S, 等待安裝 2.如何在vue當中使用element-ui的組件 1.在main.js中引入element相關的js和cssimport Vue from vueimport ElementUI from element-u…

NodeJS+Express+Mysql+MongoDB之環境配置

node作為一款可以兼容前后端的js語言,在做持久層操作上和Java比較類似,下面就簡單介紹一下項目中的數據庫配置操作. 首選使用express框架自動創建一個測試項目,并在目錄下建立一個專門存放數據庫配置的配置文件,比如:db.js 代碼如下 /* * 數據庫配置文件 * Author: zth * D…