贪心算法总结
Posted BadJui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法总结相关的知识,希望对你有一定的参考价值。
目录
一、 排队接水 1
二、 均分纸牌 2
三、 最大整数 3
贪心算法总结
一、排队接水
-
题解:
这是一个贪心算法的问题。我们需要根据每个人接水的时间来确定排队顺序,使得平均等待时间最小。具体做法如下:
- 对于每个人,计算他们在其他所有人都已经接完水之后还需要等待多长时间,即累加前面所有人的接水时间。
- 根据每个人需要等待的时间,将所有人按照从小到大的顺序进行排序。
- 输出排序后的顺序和对应的平均等待时间。
-
源码:
#include<bits/stdc++.h> using namespace std; long n,i,j,c,t,a[1000],b[1000]; float s; int main() cin>>n; for (i=1;i<=n;i++) cin>>a[i]; b[i]=i; for (i=1; i<=n-1; i++) for (j=i+1; j<=n; j++) if (a[i]>a[j]) swap(a[i],a[j]); swap(b[i],b[j]); for (i=1; i<=n;i++) c=c+a[i]; s=s+c; for (i=1; i<=n; i++)cout<<b[i]<<" "; cout<<endl<<setprecision(2)<<setiosflags(ios::fixed)<<s/n;
二、均分纸牌
-
题解:
- 计算所有纸牌数量的平均值。
- 将超过平均值的堆向不足平均值的堆移动纸牌,直到所有堆的纸牌数都相等。
- 对于超过平均值的堆,优先将多余的纸牌移动到相邻左边或右边的堆中,以此保证每次移动的次数最少。
-
源码:
#include<bits/stdc++.h> using namespace std; int n,a[10010],avg,step; int main() cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; avg+=a[i]; avg/=n; int i=1,j=n; while(a[i]==avg&&i<=n) i++; while(a[j]==avg&&j>=1) j--; while(i<j) a[i+1]=a[i]+a[i+1]-avg; a[i]=avg; step++; i++; while(a[i]==avg&&i<=n)i++; cout<<step; return 0;
三、最大整数
-
题解:
这是一个排序问题。我们需要将给定的 n 个正整数组成最大的多位整数,可以先将这些数转化为字符串,然后进行比较和排序。
具体做法如下:
- 将给定的 n 个正整数转化为字符串。
- 对于任意两个字符串 s1 和 s2,判断它们拼接在一起的结果 s1+s2 和 s2+s1 哪个更大,将其作为 s1 和 s2 的大小比较结果。
- 根据比较结果对所有字符串进行排序。
- 将排序后的字符串依次拼接起来,得到最大的多位整数。
-
源码:
#include<bits/stdc++.h> using namespace std; string a[30]; int n; bool cmp(string s1,string s2) return s1+s2>s2+s1; int main() cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++)cout<<a[i]; return 0;
leetcode之贪心算法刷题总结4
leetcode之贪心算法刷题总结4
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择,就能得到问题的答案。贪心算法需要充分挖掘题目中条件,没有固定的模式,解决有贪心算法需要一定的直觉和经验。
贪心算法不是对所有问题都能得到整体最优解。能使用贪心算法解决的问题具有「贪心选择性质」。「贪心选择性质」严格意义上需要数学证明。能使用贪心算法解决的问题必须具备「无后效性」,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
1-最长快乐字符串
题目链接:题目链接戳这里!!!
思路:构造数组,按照每个字符的个数由多到少实时排序,定义StringBuilder对象sb,如果sb的最后两位是数量最多的字符,则此时需要使用数量次多的字符,如果sb最后两位不是数量最多的字符,则在sb后追加数量最多的字符。
class Solution
public String longestDiverseString(int a, int b, int c)
//构造数组,按每个字符的个数实时排序
int [][] arr = 'a',a,'b',b,'c',c ;
StringBuilder sb = new StringBuilder() ;
while(true)
Arrays.sort(arr, (x, y) -> y[1] - x[1]);
if(arr[0][1]==0)
break ;
int n = sb.length() ;
if(n>=2 && sb.charAt(n-1) == arr[0][0] && sb.charAt(n-2)==arr[0][0])
if(arr[1][1]==0)
break ;
sb.append((char)arr[1][0]) ;
arr[1][1] -- ;
else
sb.append((char)arr[0][0]) ;
arr[0][1] -- ;
return sb.toString() ;
2-交换一次的先前排列
题目链接:题目链接戳这里!!!
思路:一次交换(交换两数字 arr[i] 和 arr[j] 的位置)后得到的、按字典序排列小于 arr 的最大可能排列。从后向前找逆序,找到逆序后与右面最大的一个值交换即可,如果最大的有多个,交换最前面的最大的。
class Solution
public int[] prevPermOpt1(int[] arr)
//从后向前找逆序,找到逆序后与右面最大的一个值交换即可,如果最大的有多个,交换最前面的最大的
for(int i=arr.length-1; i>0; i--)
if(arr[i] < arr[i-1])
for(int j=arr.length-1; j>=i; j--)
if(arr[i-1]>arr[j] && arr[j] != arr[j-1])
int temp = arr[i-1] ;
arr[i-1] = arr[j] ;
arr[j] = temp ;
return arr ;
return arr ;
3-数组大小减半
题目链接:题目链接戳这里!!!
思路:map统计所有数字出现的次数,同时map的键存入优先队列,同时优先队列按照值降序排序,每次累加值,如果sum大于数组长度的一半,则结束循环。
class Solution
public int minSetSize(int[] arr)
int ans = 0, sum = 0 , n = arr.length ;
Map<Integer, Integer> map = new HashMap<>() ;
for(int num : arr)
map.put(num, map.getOrDefault(num, 0) + 1) ;
//优先队列,按照值降序排序
PriorityQueue<Integer> heap = new PriorityQueue<>((o1,o2)->map.get(o2)-map.get(o1));
heap.addAll(map.keySet()) ; //队列中存入键,按照值排序
while(sum < n / 2)
sum += map.get(heap.poll()) ;
ans ++ ;
return ans ;
这样写也是可以的,哈哈哈,思路一样,换成数组模拟,效率更高。用cnt记录每个数字出现的次数,然后对cnt升序排序,然后从后向前遍历cnt,判断数组是否减半即可。
class Solution
public int minSetSize(int[] arr)
int ans = 0, sum = 0 , n = arr.length ;
int [] cnt = new int [100001] ;
for(int num : arr)
cnt[num] ++ ;
Arrays.sort(cnt) ;
for(int i=cnt.length-1; i>=0; i--)
if(sum >= n / 2)
break ;
sum += cnt[i] ;
ans ++ ;
return ans ;
4-和为K的最少斐波那契数字数目
题目链接:题目链接戳这里!!!
思路:每一次从前向后找那个小于k的最大斐波那契数字,然后用k减取它,直至k等于0.
class Solution
public int findMinFibonacciNumbers(int k)
int ans = 0, f1 = 1, f2 = 1 ;
while(k!=0)
f1 = f2 = 1 ;
while(f2<=k)
f2 += f1 ;
f1 = f2-f1 ;
k -= f1 ;
ans ++ ;
return ans ;
5-构造K个回文字符串
题目链接:题目链接戳这里!!!
思路:这题技巧性很强,不太容易想到哦,判断能否将字符串构建成k个回文字符串,如果能构成需要满足两个条件。
第一:s的长度大于等于k
第二:s中奇数字符串的长度小于等于k
class Solution
public boolean canConstruct(String s, int k)
//s的长度大于等于k且s中奇数字符的个数小于等于k
int [] cnt = new int [26] ;
int n = s.length() ;
for(int i=0; i<n; i++) //统计每个字符出现的次数
cnt[s.charAt(i)-'a'] ++ ;
int odd = 0 ;
for(int i=0; i<26; i++)
if(cnt[i] % 2 == 1)
odd ++ ;
return odd<=k && n>=k ;
6-检查一个字符串是否可以打破另一个字符串
题目链接:题目链接戳这里!!!
思路:对两个字符串分别进行升序排序,然后分别比较是否满足条件即可。
对于所有c1[i]>=c2[i] 或者 对于所有的c1[i]<=c2[i]都是满足条件的。
class Solution
public boolean checkIfCanBreak(String s1, String s2)
char [] c1 = s1.toCharArray() ;
char [] c2 = s2.toCharArray() ;
Arrays.sort(c1) ;
Arrays.sort(c2) ;
return f(c1,c2) || f(c2,c1) ;
public boolean f(char [] c1, char [] c2)
for(int i=0; i<c1.length; i++)
if(c1[i]<c2[i])
return false ;
return true ;
7-不同整数的最少数目
题目链接:题目链接戳这里!!!
思路:这题很好想,不太好实现,因为删除k次,使得不同的数字的数目变的最少,所以每次删除数字中出现次数最少的数。用map存储每个数字的出现次数,建立优先队列,按照map中值的由小到大排序,只有当map中的值为0时候,ans才累加。
class Solution
public int findLeastNumOfUniqueInts(int[] arr, int k)
Map<Integer, Integer> map = new HashMap<>() ;
for(int num : arr) //记录每个数字出现的个数
map.put(num, map.getOrDefault(num, 0) + 1) ;
int ans = 0 ;
int n = map.keySet().size() ;
boolean flag = true ;
PriorityQueue<Integer> queue = new PriorityQueue<>((o1,o2)->map.get(o1)-map.get(o2));
queue.addAll(map.keySet()) ;
while(k>0)
int value = map.get(queue.element()) ;
k -- ;
value -= 1 ;
if(value==0)
map.remove(queue.element()) ;
queue.poll() ;
flag = true ;
else
map.put(queue.peek(), value) ;
flag = false ;
if(flag)
ans ++ ;
return n-ans ;
8-非递增顺序的最小子序列
题目链接:题目链接戳这里!!!
思路:前缀和+贪心
对数组元素升序,然后先求出所有元素的前缀和,避免后面重复求和,从后向前遍历数组元素,累加当前元素,如果当前元素和,大于前面则结束循环。
class Solution
public List<Integer> minSubsequence(int[] nums)
Arrays.sort(nums) ;
int [] sums = new int [nums.length+1] ;
for(int i=1; i<sums.length; i++)
sums[i] = nums[i-1] + sums[i-1] ;
List<Integer> ans = new ArrayList<>() ;
int sum = 0 ;
for(int i=nums.length-1; i>=0; i--)
sum += nums[i] ;
ans.add(nums[i]) ;
if(sum>sums[i])
break ;
return ans ;
9-最少的后缀翻转次数
题目链接:题目链接戳这里!!!
思路:每次相邻的不一样,则需要向后翻转一次,首先要记录第一位是否需要翻转。
class Solution
public int minFlips(String target)
int ans = 0 ;
if(target.charAt(0)=='1') //判断第一位是否需要翻转
ans ++ ;
for(int i=0; i<target.length()-1; i++)
if(target.charAt(i) != target.charAt(i+1))
ans ++ ;
return ans ;
10-三次操作后最大值与最小值得最小差
题目链接:题目链接戳这里!!!
思路:这个操作过程其实就是删除过程,三次操作,保证最大值和最小值得差最小,那么每次都对数组中得最大值或者最小值进行操作是最优得选择,故三次操作分为四种情况:
四种情况:删除三个最大,删除三个最小,删除两个最大一个最小,删除一个最大两个最小
class Solution
public int minDifference(int[] nums)
if(nums.length<=4)
return 0 ;
Arrays.sort(nums) ;
//三次操作,四种情况,删除三个最大,删除三个最小,删除两个最大一个最小,删除一个最大两个最小
int a = Math.min(nums[nums.length-4] - nums[0], nums[nums.length-1]-nums[3]);
int b = Math.min(nums[nums.length-3] - nums[1], nums[nums.length-2] - nums[2]) ;
return Math.min(a,b) ;
11-你可以获得的最大硬币数目
题目链接:题目链接戳这里!!!
思路:保证你可以获得最大硬币数额,那就要先对数组进行升序排序,然后每次取出两个最大和一个最小,这样最后可以保证你拿到的硬币数额最大。
class Solution
public int maxCoins(int[] piles)
Arrays.sort(piles) ;
int ans = 0 ;
//两个人最大配一个最小
int j = 0 ;
for(int i=piles.length-2; i>=j; i-=2)
ans += piles[i] ;
j ++ ;
return ans ;
12-字符频次唯一的最小删除次数
题目链接:题目链接戳这里!!!
思路:count数组记录每个字符出现的频次,对于每个字符,如果频次之前没有出现过,则不需要管,如果之前出现过,则需要将当前频次减1,直至频次没有出现。
class Solution
public int minDeletions(String s)
int [] count = new int [26] ;
for(int i=0; i<s.length(); i++) //记录每个字符出现的次数
count[s.charAtleetcode之贪心算法刷题总结4