HDU6438 Buy and Resell 解题报告(一个有趣的贪心问题的严格证明)
写在前面
此题是一个很容易想到的贪心题目,但是正确性的证明是非常复杂的。然而,目前网上所有题解并未给出本题贪心算法的任何正确性证明,全部仅停留在描述出一个贪心算法。本着对算法与计算机科学的热爱(逃),我花了2周时间深入研究了这个问题,并请教了Apass.Jack 大牛,终于在他的帮助下证明了该贪心的正确性。接下来将给出详细地证明过程。
PS:Apass.Jack提供了整个证明框架(尽管后来被我发现了一处错误并重新修正了证明),在此表示感谢!
题目描述
给定$n$($n \le 10^5)$个城市的物品价格,每个城市只能最多购买或卖出一个物品,可以既不买也不卖。当然,若当前手上没有物品则不能卖出。问从城市1走到城市$n$,途中进行买卖后最多赚多少钱,在此前提下最少进行多少次交易。初始手上没有任何物品,但有无限的钱。
Sample Input
Sample Output
算法描述
我们先不考虑最小化交易次数,只考虑利润最大化。本题有一个比较好想的贪心算法:
从左往右遍历城市,并维护每个城市当前的状态(买了物品、卖了物品或什么都不做)。对于城市$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,故算法是正确的。
最小化交易次数
根据上面定理,当价格互不相同时最优策略是唯一的,但价格可以相同时则不一定,此时才存在最小化交易次数的问题。此时考虑修改算法,当优先队列中最低价格不止一个时,优先取原本卖出物品的城市,这样该城市的状态会被修改成什么都不做。这个正确性很显然。对于价格相同的物品,它们对于后续城市是完全等价的,因此尽可能的将它们变成不买也不卖必然达到最小交易次数。
综上,这道题目就完全解决了。
参考代码
#include<cstdio>
#include<queue>
using namespace std;
int p[];
struct Node{
int i;
bool sell;
bool operator < (const Node t)const{
return p[i] != p[t.i] ? p[i] > p[t.i] : !sell && t.sell;
}
};
int main()
{
int test, n;
scanf("%d", &test);
while (test--){
priority_queue<Node> q;
scanf("%d", &n);
for (int i = ; i < n; i++)
scanf("%d", &p[i]);
long long ans = ;
int cnt = ;
q.push({ , });
for (int i = ; i < n; i++){
int j = q.top().i;
if (p[j] < p[i]){
bool sell = q.top().sell;
q.pop();
ans += p[i] - p[j];
if (sell)q.push(Node{ j, });
else cnt += ;
q.push(Node{ i, });
}
else q.push(Node{ i, });
}
printf("%lld %d\n", ans, cnt);
}
}
HDU6438 Buy and Resell 解题报告(一个有趣的贪心问题的严格证明)的更多相关文章
- hdu6438 Buy and Resell 买卖物品 ccpc网络赛 贪心
题目传送门 题目描述: 有n座城市,每座城市都可以对一个物品进行一次的买进或者卖出,可以同时拥有多个物品,计算利润最大值,并且交易次数要最少.(买入卖出算两次操作) 思路: 建立两个小根堆 优先队列, ...
- 2018中国大学生程序设计竞赛 - 网络选拔赛 hdu6438 Buy and Resell 买入卖出问题 贪心
Buy and Resell Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)To ...
- 2018中国大学生程序设计竞赛 - 网络选拔赛 1001 - Buy and Resell 【优先队列维护最小堆+贪心】
题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6438 Buy and Resell Time Limit: 2000/1000 MS (Java/O ...
- hdu6438 Buy and Resell
多少年不写题了... (我把每一天看作是一个商品,第i天是第i个商品) 一开始看了半天看出来一个性质:买的所有商品中最贵的不会比卖的所有商品中最便宜的贵,然后似乎没有什么用处.... 所以最后还是看题 ...
- HDU6438 Buy and Resell 2018CCPC网络赛 -低买高卖-贪心经典题
目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 Catalog Problem:Portal传送门 原题目描述在最下面. 出过很多次:5 ...
- POJ3069 POJ2586 解题报告(异曲同工的贪心算法)
[POJ 3069](2586见下) 原题在此:http://poj.org/problem?id=3069 题目大意: 一个直线上有N个点.点i的距离是Xi.从这些点中选取若干个加上标记.要求:对于 ...
- 【剑指Offer】二叉树的下一个结点 解题报告(Python)
[剑指Offer]二叉树的下一个结点 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interviews ...
- 【第40套模拟题】【noip2011_mayan】解题报告【map】【数论】【dfs】
目录:1.潜伏者 [map] 2.Hankson的趣味题[数论]3.mayan游戏[dfs] 题目: 1. 潜伏者(spy.pas/c/cpp)[问题描述]R 国和S 国正陷入战火之中,双方都互派间谍 ...
- USACO Section1.3 Wormholes 解题报告
wormhole解题报告 —— icedream61 博客园(转载请注明出处)------------------------------------------------------------- ...
随机推荐
- 将数据库数据添加到ListView控件中
实现效果: 知识运用: ListView控件中的Items集合的Clear方法 //从listView控件的数据项集合中移除所有数据项 补充:可以使用Remove或RemoveAt方法从集合中移除单个 ...
- OO第13-14次作业总结
目录 面向对象第13-14次作业总结博客 1.设计分析 2.架构总结.测试 3.课程收获和建议 面向对象第13-14次作业总结博客 1.设计分析 这个单元是我做的最差的一个单元.总工程量超过2000行 ...
- Websocket教程SpringBoot+Maven整合(详情)
1.大话websocket及课程介绍 简介: websocket介绍.使用场景分享.学习课程需要什么基础 笔记: websocket介绍: WebSocket协议是基于TCP的一种新的网络协议.它实现 ...
- XCode5 使用AutoLayout情况下改变控件的 方法
[self.viewButtonsetTranslatesAutoresizingMaskIntoConstraints:NO]; //[self.view addConstraint:[NSLayo ...
- 2018.11.7 Nescafe29 T1 穿越七色虹
题目 题目背景 在 Nescafe27 和 28 中,讲述了一支探险队前往 Nescafe 之塔探险的故事…… 当两位探险队员以最快的时间把礼物放到每个木箱里之后,精灵们变身为一缕缕金带似的光,簇簇光 ...
- shell数组脚本
#!/bin/bash array=( ) ;i<${#array[*]};i++)) do echo ${array[i]} done 脚本2 #!/bin/bash array=( ) fo ...
- js浮点数加减乘除
浮点数精确计算 /** ** 加法函数,用来得到精确的加法结果 ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显.这个函数返回较为精确的加法结果. ** 调用:ac ...
- 【JavaScript】修改图片src属性切换图片
今天做项目时其中一个环节需要用到js修改图片src属性切换图片,现在来记录一下 以下是示例: html <img src="/before.jpg" id="img ...
- win7在某个盘或文件夹中出现右键只能新建文件夹的情况 (2012-12-28-bd 写的日志迁移
至于只能新建文件夹的情况如图: 解决方法是在运行中输入msconfig进入如图: 在系统设置选工具项在选中更改UAC设置点击启动如图: 如图: 直接把通知栏拉到最低确定即可(如果已经是最低了那就随便改 ...
- JZOJ 3509. 【NOIP2013模拟11.5B组】倒霉的小C
3509. [NOIP2013模拟11.5B组]倒霉的小C(beats) (File IO): input:beats.in output:beats.out Time Limits: 1000 ms ...