贪心算法总结

Posted BadJui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法总结相关的知识,希望对你有一定的参考价值。

目录

 

一、 排队接水 1

二、 均分纸牌 2

三、 最大整数 3

 

贪心算法总结

一、排队接水

  • 题解:

这是一个贪心算法的问题。我们需要根据每个人接水的时间来确定排队顺序,使得平均等待时间最小。具体做法如下:

  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;

 

 

二、均分纸牌

  • 题解:

  1. 计算所有纸牌数量的平均值。
  2. 将超过平均值的堆向不足平均值的堆移动纸牌,直到所有堆的纸牌数都相等。
    • 对于超过平均值的堆,优先将多余的纸牌移动到相邻左边或右边的堆中,以此保证每次移动的次数最少。
  • 源码:

#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 个正整数组成最大的多位整数,可以先将这些数转化为字符串,然后进行比较和排序。

具体做法如下:

  1. 将给定的 n 个正整数转化为字符串。
  2. 对于任意两个字符串 s1 和 s2,判断它们拼接在一起的结果 s1+s2 和 s2+s1 哪个更大,将其作为 s1 和 s2 的大小比较结果。
  3. 根据比较结果对所有字符串进行排序。
  4. 将排序后的字符串依次拼接起来,得到最大的多位整数。
  • 源码:

#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

贪心算法:总结篇!(每逢总结必经典)

leetcode之贪心算法刷题总结5

贪心算法总结

疯子的算法总结(四)贪心算法

贪心算法千字总结