例题一:
题目:
思路:
首先来看得知可以大胆使用暴力法
根据上文(简单到爆炸der贪心算法学习及其证明方法其一:交换论证法-CSDN博客)贪心算法的思路来思考,第一步便是将其分成多个步骤,分别对其操作。显而易见,分成的步骤即是,每一次选定一个数除二。
第二步便是思考如何对该步骤实现。
第三步便是期望得出最优解。
实现步骤
分步完成后,来想第二步该如何实现,由题意可知我们要得到最小的操作数,则我们要力求最少次数剪掉一半大小,即分到每一步就要尽可能的剪掉所能剪掉的最大值。so~我们要先找到最大值,而如果每一次剪完后都用一次排序未免也太大了,会导致超时(这里我试了,即使用的快速排序)。
class Solution {
double num[(int)1e5 + 2] = { 0 };
public:
int halveArray(vector<int>& nums) {
int j = 0;
double sum = 0, atp, count = 0;
//priority_queue<double> num;
for (int i : nums)
{
sum += (double)i;
//num.push((double)i);
num[(int)j] = i;
j++;
}
sum /= 2;
while (sum > 0)
{
sort(num,num+(int)j);
atp = num[j-1];
atp /= 2;
num[j-1] = atp;
sum -= atp;
// atp=num.top();
//num.pop();
//atp/=2;
//num.push(atp);
//sum-=atp;
count++;
}
return (int)count;
}
};
虽然能对简单数据进行正确处理但对于大的数据就寄。
所以我们最好使用priority_queue,他能够将存进去的数据自动进行排序,按照一定的顺序,默认的大小规则是队列顶端最大。则我们只需要将队列顶端拿出来除二再放回去,并计数即可。
参考代码:
class Solution {
public:
int halveArray(vector<int>& nums) {
double sum = 0, atp, count = 0;
priority_queue<double> num;
for (int i : nums)
{
sum += (double)i;
num.push((double)i);
}
sum /= 2;
while (sum > 0)
{
atp=num.top();
num.pop();
atp/=2;
num.push(atp);
sum-=atp;
count++;
}
return count;
}
};
证明:交换论证法(上篇文章说过,链接在上文提过)
最优解的每一个步骤与贪心解有两种不一样的可能,这里我们假设贪心解在该步骤操作的是x,最优解操作的是y,但是可以知道x>=y.
1.最优解在全过程中没有用过x,也就是最优解没有操作过最大值,在没有操作过最大值情况也得到了最优解,那么如果换成最大值操作那么甚至每步可以减的更多,所以可以大胆将y替换为x
2.最优解在全过程中用过x,那么我们可以调换最优解过程中后续的x和现在的y的位置,来实现向贪心解靠拢。
由此我们可以统过最优解且不改变性质的情况下得到了贪心解的解法。
例题二:
题目:
思路:
注:此题有点狗贼,让我栽了个跟头(虽然主要怪我太菜)
第一步:分成多个步骤,那么每一步就是判断两个数字,哪个数字放在前面更好。直到把所有数字排在应该在的位置
第二步:实现判断这两个数字谁放在前面更大,并排定两个数字的位置
第三步:向今天凌晨的双子座流星雨祈愿自己写的小代码能成功
(这里说一下我刚开始的错误思路,万一有读者朋友和我想的一样,我就算是给你探探路辣)
(我想的是通过冒泡嘛,两次循环嵌套,每一次具体判断是通过两个数转化为同样位数大小后判)(断这两个数谁更大,比如521和1314转化为5210和1314然后5210大,就让521排前面,想的)
(很好对吧?结果,结果有个案例是11311和113,然后实现的结果就是11311113但是正确答案)(就是11311311,结果寄了。)
实现步骤:
就是将vector里的整型数据转换成字符串类型,然后再对其通过字符串判断大小来判断,当然也可以通过冒泡思想通过两个for循环嵌套来干他,但咱们有高贵的快排,咱们只需要对比较参数函数修改一下就行了,这里因为比较函数只用了一次,我就直接用的lanmda函数。返回a+b>b+a的大小判断情况,如果自己写冒泡排序不能只写大于小于情况还要再写具体的将谁放在谁前面。
参考代码:
class Solution {
public:
string largestNumber(vector<int>& nums) {
vector<string> num;
for(auto i:nums)num.push_back(to_string(i));
sort(num.begin(),num.end(),[](string &a,string &b){
return a+b>b+a;
});
string ret;
for(auto i:num)ret+=i;
if(ret[0]=='0')return "0";
return ret;
};
};
哦,简短有力的代码。
证明(全序关系):
这里用到了离散数学的全序关系概念
全序关系
用处:从集合中任意挑选两个元素,在我定义的排列规则下满足全序关系,则得到我们定义的规则可以用来排序。
若满足三个性质则满足全序关系
1.完全性
集合任意元素都可以进行大小比较
2.反对称性
任意两个元素,若满足a>=b&&a
例如一个上升趋势的函数,a在b前面,则a>=b,但也可以让a在b后面,则a
否则该集合不能排序得出唯一的结果。
3.传递性
任意挑出来三个元素,a>b,b>c必须能够得出a>c。
则这里证明符合全序关系即可,这里任意从字符举例a,b排成的ab和ba都可以比较大小,没毛病,第一个性质满足
这里假设ab<=ba&&ba<=ab,
暂时看作整型时,则a*10^x+b*10^y次方(假设a数字有x-y位,b数字有y位)<=b*10^x+a*10^y&&
a*10^x+b*10^y次方>=b*10^x+a*10^y,这样看有点晦涩,但是举个例子,521的10^3加上231>=521的10^3加上231&&521的10^3加上231<=521的10^3加上231,所以521231==521231,ab==ba;
第二个性质,满足
第三个性质满足。