贪心算法简要介绍:https://oi-wiki.org/basic/greedy/
1.贪心算法基本原理
1)描述
贪心算法(又称贪婪算法)是指:在对问题求解时,总是做出在当前看来是最好的选择。也就是说不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。(不关注整体只关注局部)
(正确的废话,但是没用)
贪心算法的关键:贪心策略的选择。
2)适用情况
贪心算法在有最优子结构的问题中尤为有效。最优子结构的意思是问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。
(1)适用情况
假设只能拿一张,肯定是拿最大的,所以可以把问题拆解成那三次一张的纸币获得的最大值
(2)不适用情况
你有一个承重为8的背包,在不超过背包承重上限的情况下,你能获得的物品最大总价值是多少?(01背包问题)
贪心算法:考虑局部最优,先选择价值最大的,即选择物品一,此时背包最多可以装重量为2的物品,所以结果是9,显然不对。
通过以上两个例子不能说贪心算法只能解决比较的简单问题。
由于贪心算法的算法框架中没有套路,因此在设计贪心算法时可用的思维抓手比较少,导致设计贪心算法时,依靠灵感。(比较需要天赋)
3)贪心算法证明
4)贪心与偏序关系
(1)偏序关系
符合贪心算法思想。
(2)形式定义
传递性,通常情况下,在贪心算法设计中解释了为什么贪心算法局部最优,最终保证局部最优。
(3)联系
在贪心算法设计中,主要寻找偏序关系,最后加以证明,那么贪心算法就设计出来了。
eg:HZOJ-256国王游戏---https://oj.haizeix.com/problem/256
假设排为以下顺序:(调换i与i+1位置的大臣)
如果第二个序列优于第一个序列,则乘积越大的越往前排,它不会使得我所得到的方案变差。(偏序关系在贪心算法中的体现)
5)学习心法
在一次又一次的贪心策略证明中掌握:贪心策略的选择。
2.练习题
1)HZOJ-505最大整数
--https://oj.haizeix.com/problem/505
局部:A+B>B+A,则A排在B前面
整体:按照如上策略得到的排序以后的序列就是最大整数。
代码如下所示:
#include<bits/stdc++.h>
using namespace std;
bool cmp(const string &a,const string &b){
return a+b>b+a;
}
int main(){
int n;
vector<string> arr;//字符串数组
string s;
cin>>n;
for(int i=0;i<n;i++){
cin>>s;
arr.push_back(s);
}
sort(arr.begin(),arr.end(),cmp);
for(int i=0;i<n;i++){
cout<<arr[i];
}
cout<<endl;
return 0;
}
贪心策略证明(之后的题不再做证明):
证明1:A+B>B+A(+字符串的连接),为什么?
情况1:A>B(基于字典序--按位进行比较)
eg:123,96 数字中123>96 字典序:96>123,按位比较9>1,因此96>123
不用证明,这是显而易见的。
情况2:A<B且A不是B的前缀
这种情况是不可能的。
情况3:A是B的前缀
那么黄色部分全相等。
如果A+B>B+A,则红色部分的字符<绿色部分的字符。
证明2:当A+B>B+A,为什么A排到B前面,能保证全局最优。
假设:排序以后为:A、C、B
反证法:假设,最优策略不是A、C、B,而是B、C、A;
两种字符串:
因为排序以后为:A、C、B所以红色部分字典序小于绿色部分,因此BCA一定小于ACB,所以当A排在B前面总是优于B排在A前面。
2)HZOJ-504-删数
--https://oj.haizeix.com/problem/504
局部:每次删除一个离最高位最近的逆序位的第一个数字1234321,其中43是离最高位最近的逆序位,删除4即可。
整体:按照如上策略执行n次以后,得到的就是最小的整数。
代码如下所示:
#include<bits/stdc++.h>
using namespace std;
bool cmp(const char &a,const char &b){
return a<b;
}
int main(){
int n;
char s[505];
cin>>s>>n;
for(int i=0;i<n;i++){
int j=0;
while(s[j+1]!='\0'&&s[j]<=s[j+1])j++;
while(s[j])s[j]=s[j+1],j++;
}
for(int i=0,flag=1;s[i];i++){
if(s[i]=='0'&&flag)continue;
cout<<s[i];
flag=0;
}
cout<<endl;
return 0;
}
3)HZOJ-503-独木舟
--https://oj.haizeix.com/problem/503
局部: 每次安排,如果最重的人和最轻的人能坐一起,就坐一条独木舟,否则最重的人自己坐一条船。
整体:按照如上策略执行,就能得到最少的独木舟。
代码如下所示:
#include<bits/stdc++.h>
using namespace std;
const int N=30010;
bool cmp(const char &a,const char &b){
return a<b;
}
int a[N];
int main(){
int w,n;
cin>>w>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
int ans=0;
for(int i=1,j=n;i<=j;){
if(i!=j){
if(a[i]+a[j]<=w)ans++,i++,j--;
else ans++,j--;
}else{
ans++;
break;
}
}
cout<<ans<<endl;
return 0;
}
4)HZOJ-258-最大子阵和
--https://oj.haizeix.com/problem/258
局部:s代表以前一个位置为结尾的最大子序和,当前值为 a则s>=0,s+=a否则s=a。
整体:按照如上策略执行,过程中s的最大值,就是最大子序和。
代码如下所示:
#include<bits/stdc++.h>
using namespace std;
const int N=110;
bool cmp(const char &a,const char &b){
return a<b;
}
int a[N][N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
a[i][j]+=a[i-1][j];//从1~i行第j列的和值
}
}
int ans=0;
for(int i=1;i<=n;i++){//起始行号
for(int j=i;j<=n;j++){//终止行号
int s=0;
for(int k=1;k<=n;k++){
int x=a[j][k]-a[i-1][k];//从i~j行第k列的和值
if(s>=0)s+=x;
else s=x;
if(s>ans)ans=s;
}
}
}
cout<<ans<<endl;
return 0;
}
5)HZOJ-511-最少操作次数
https://oj.haizeix.com/problem/511
局部:ans代表最少操作次数,则ans更新策略如下:
情况1:若 a * k <= b ,ans += 1 + b % k , b /= k;
情况2:若 a * k > b , ans += (b - a) , 终止;
整体:按照如上策略执行,最终得到的ans,就是最少操作次数。
补充(进制):将 a 和 b 看成一个 k 进制的数,a * k 就是将a的 k 进制形式向左平移一位,末尾加个 0。
结论1:使用 * k 优于 + 1 操作(eg : a=3,b=10,k=3------a*k+1=10只需要两次,a+1*7=10,需要7次)
结论2:当 a 为 b 的数字前缀时,再使用 * k 操作。
eg : b ( k 进制形式 ):1 4 2 3
a ( k 进制形式 ): 1 2
a -> b : +1 +1 * k +1 +1 * k +1 +1 +1
对应: [ b - a] [ 1 + b % k] [ 1 + b % k ]
代码如下所示:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){
ll a,b,k;
cin>>a>>b>>k;
ll ans=0;
if(k==0){
if(b==0)cout<<(ll)min(a,(ll)1)<<endl;
else cout<<(b-a)<<endl;
return ;
}
if(k==1){
cout<<(b-a)<<endl;
return ;
}
while(1){
if(a*k<=b)ans+=1+b%k,b/=k;
else {
ans+=(b-a);
cout<<ans<<endl;
return ;
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}