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.mzph.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…