贪心算法在笔试中的解题套路
1.实现一个不依靠贪心策略的解法X(可以确保正确的算法),可以用最暴力的尝试
2.脑补出贪心策略A、贪心策略B、贪心策略C........
3.用解法X和对数器,去验证每一个贪心策略,用实验的方式得知哪个贪心策略正确
4.不要去纠结贪心策略的证明。
贪心策略在实现时,经常用到的技巧
1.根据某标准建立一个比较器来排序
2.根据某标准建立一个比较器来组成堆
贪心算法题目
最大连续1的个数 II
贪心策略:
以 0 为分割,遍历数组,不断更新一个0两侧的连续1的数量+1(0反转)
class Solution {
public:
int findMaxConsecutiveOnes(vector<int>& nums) {
int pre1=0;//前面连续1的个数
int i=0;
int n=nums.size();
while(i<n&&nums[i]==1){
pre1++;
++i;
}
int res=0;
int pre0=i;//前一个0的位置
if(i==n)return n;
i++;
while(i<n){
if(nums[i]==1){
++i;
continue;
}
res=res>i-pre0+pre1?res:i-pre0+pre1;
pre1=i-pre0-1;
pre0=i;
++i;
}
res=res>n-pre0+pre1?res:n-pre0+pre1;
return res;
}
};
无重叠区间
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
//贪心: 会议室问题变形
//需要移除区间的最小数量=总共的区间数-最大不重叠区间数(最多满足的会议)
//首先需要进行排序:
sort(intervals.begin(),intervals.end(),[&](vector<int>&o1,vector<int>&o2){
if(o1[1]!=o2[1]){
return o1[1]<o2[1];
}
return o1[0]<o2[0];
});
int cnt=0;
int end=INT_MIN;
for(auto o:intervals){
if(o[0]>=end){
end=o[1];
cnt++;
}
}
return intervals.size()-cnt;
}
};
使每位学生都有座位的最少移动次数
贪心策略:
经过排序后,1号学生就坐1号座位, 2号学生就坐2号座位 …
class Solution {
public:
int minMovesToSeat(vector<int>& seats, vector<int>& students) {
std::sort(seats.begin(),seats.end());
std::sort(students.begin(),students.end());
int ans=0;
for(int i=0;i<seats.size();++i){
ans+=std::abs(seats[i]-students[i]);
}
return ans;
}
};
最多删除一个字符得到回文
class Solution {
public boolean process(String s,int left,int right,boolean isOk){
if(left>=right){
return true;
}
if(s.charAt(left)==s.charAt(right)){
return process(s,left+1,right-1,isOk);
}
boolean ans=false;
if(!isOk)return false;
if(s.charAt(left+1)==s.charAt(right)){
ans|=process(s,left+2,right-1,false);
}
if(s.charAt(left)==s.charAt(right-1)){
ans|=process(s,left+1,right-2,false);
}
return ans;
}
public boolean validPalindrome(String s) {
return process(s,0,s.length()-1,true);
}
}
心算挑战
class Solution {
public int maxmiumScore(int[] cards, int cnt) {
//minOdd,midEven已取的最小奇数和偶数
int minOdd=Integer.MAX_VALUE;
int minEven=Integer.MAX_VALUE;
Arrays.sort(cards);
int ans=0;
for(int i=cards.length-1;i>=cards.length-cnt;--i){
if((cards[i]&1)==0){
minEven=Math.min(minEven,cards[i]);
}else{
minOdd=Math.min(minOdd,cards[i]);
}
ans+=cards[i];
}
if((ans&1)==0)return ans;
int tmp=ans;
//返回去掉最小偶数+最大的奇数,去掉最小奇数+最大偶数
tmp-=minOdd;
int i;
for(i=cards.length-cnt-1;i>=0;--i){
if((cards[i]&1)==0){
break;
}
}
if(i>=0){
tmp+=cards[i];
}else{
tmp=0;
}
if(minEven==Integer.MAX_VALUE){
return Math.max(0,tmp);
}
ans-=minEven;
for(i=cards.length-cnt-1;i>=0;--i){
if((cards[i]&1)==1){
break;
}
}
if(i>=0){
ans+=cards[i];
}else{
ans=0;
}
return Math.max(ans,tmp);
}
}
拆分数位后四位数字的最小和
class Solution {
public int minimumSum(int num) {
int[] arr=new int[4];
arr[0]=num%10;
arr[1]=num/10%10;
arr[2]=num/100%10;
arr[3]=num/1000;
Arrays.sort(arr);
int ans=0;
ans=arr[3]+arr[2]+10*(arr[0]+arr[1]);
return ans;
}
}
打折购买糖果的最小开销
class Solution {
public class Compare implements Comparator<Integer>{
public int compare(Integer o1,Integer o2){
return o2-o1;
}
}
public int minimumCost(int[] cost) {
Queue<Integer>bigRootHeap=new PriorityQueue<>(new Compare());
for(Integer i:cost){
bigRootHeap.add(i);
}
int ans=0;
while(!bigRootHeap.isEmpty()){
if(bigRootHeap.size()>2){
ans+=bigRootHeap.poll();
ans+=bigRootHeap.poll();
bigRootHeap.poll();
}else{
while(!bigRootHeap.isEmpty()){
ans+=bigRootHeap.poll();
}
}
}
return ans;
}
}
长度为 K 的最大子数组
class Solution {
public int[] largestSubarray(int[] nums, int k) {
int i=0;
int max=0;
int[] ans=new int[k];
for(int j=0;j<=nums.length-k;++j){
if(nums[j]>max){
i=j;
max=nums[j];
}
}
for(int j=0;j<k;++j){
ans[j]=nums[i+j];
}
return ans;
}
}
将数组分成和相等的三个部分
class Solution {
public boolean canThreePartsEqualSum(int[] arr) {
int all=0;
for(Integer i:arr){
all+=i;
}
if(all%3!=0)return false;
int part=all/3;
int i,j;
int sum=0;
for(i=0;i<arr.length;++i){
sum+=arr[i];
if(sum==part)break;
}
sum=0;
for(j=arr.length-1;j>=0;--j){
sum+=arr[j];
if(sum==part)break;
}
if(i+1<j)return true;
return false;
}
}
增减字符串匹配
class Solution {
public int[] diStringMatch(String s) {
int[] perm=new int[s.length()+1];
int n=s.length();
int low=0;
int high=n;
for(int i=0;i<s.length();++i){
if(s.charAt(i)=='D'){
perm[i]=high--;
}else{
perm[i]=low++;
}
}
perm[n]=low;
return perm;
}
}
柠檬水找零
class Solution {
public boolean lemonadeChange(int[] bills) {
int num5=0;
int num10=0;
int num20=0;
for(int i=0;i<bills.length;++i){
if(bills[i]==5){
num5++;
}else if(bills[i]==10){
num10++;
if(num5>=1){
num5--;
}else{
return false;
}
}else{
num20++;
if(num10>=1){
if(num5<1)return false;
else {
num5--;
num10--;
}
}else if(num10==0){
if(num5<3)return false;
else num5-=3;
}
}
}
return true;
}
}
种花问题
class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {
int rest=n;
for(int i=0;i<flowerbed.length;++i){
if(rest==0)return true;
if(i==0){
if(flowerbed.length==1&&flowerbed[i]==0){
flowerbed[i]=1;
rest--;
}else if(i+1<flowerbed.length&&flowerbed[i+1]==0&&flowerbed[i]==0){
flowerbed[i]=1;
rest--;
}
}
else if(i==flowerbed.length-1){
if(flowerbed[i-1]==0&&flowerbed[i]==0){
flowerbed[i]=1;
rest--;
}
}
else if(flowerbed[i-1]==0&&flowerbed[i]==0&&flowerbed[i+1]==0){
flowerbed[i]=1;
rest--;
}
}
return rest==0?true:false;
}
}
会议室问题
注意: 在写比较器时,对于两个会议,在会议结束时间不同时,要按照 结束时间早的排前面 ,但是,当会议结束时间相同时,要按照 会议开始的时间早的排前面 (否则,通不过全部测试) 。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;
//会议室问题
public class Main {
public static class Time{
public int start;
public int end;
public Time(int start,int end) {
this.start=start;
this.end=end;
}
}
public static class Compare implements Comparator<Time>{
public int compare(Time t1,Time t2) {
if(t1.end!=t2.end) {
return t1.end-t2.end;
}
return t1.start-t2.start;
}
}
public static int ChamberNumber(Time[] list) {
int ans=0;
if(list.length==0)return 0;
ans++;
Time cur=list[0];
int endTime=cur.end;
for(int i=1;i<list.length;++i) {
cur=list[i];
if(cur.start>=endTime) {
ans++;
endTime=cur.end;
}
}
return ans;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
Time[] list=new Time[n];
for(int i=0;i<n;++i) {
list[i]=new Time(scan.nextInt(),scan.nextInt());
}
Arrays.sort(list,new Compare());
System.out.println(ChamberNumber(list));
}
}
拼接所有的字符串产生字典序最小的字符串
注意:
"
+
"
"+"
"+" 拼接字符串会运行超时,可以采用
S
t
r
i
n
g
B
u
f
f
e
r
−
s
t
r
.
a
p
p
e
n
d
(
)
StringBuffer- str.append()
StringBuffer−str.append() 方法来加速 。
另外,
s
t
r
1.
c
o
m
p
a
r
e
T
o
(
s
t
r
2
)
str1.compareTo(str2)
str1.compareTo(str2) 返回
s
t
r
1
−
s
t
r
2
str1-str2
str1−str2 的字典序差值 。
import java.util.*;
public class Solution {
/**
*
* @param strs string字符串一维数组 the strings
* @return string字符串
*/
public static class Compare implements Comparator<String>{
public int compare(String o1,String o2) {
return (o1+o2).compareTo(o2+o1);
}
}
public String minString (String[] strs) {
Arrays.sort(strs,new Compare());
StringBuffer ans=new StringBuffer("");
for(String s:strs){
ans.append(s);
}
return ans.toString();
}
}
切金条
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;
//切金条
public class Main {
public static class Compare implements Comparator<Integer>{
public int compare(Integer o1,Integer o2) {
return o1-o2;
}
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
PriorityQueue<Integer>heap=new PriorityQueue<>(new Compare());
for(int i=0;i<n;++i) {
heap.add(scan.nextInt());
}
long ans=0;//数据有可能很大
while(heap.size()>1) {
int a=heap.poll();
int b=heap.poll();
heap.add(a+b);
ans+=(a+b);
}
System.out.println(ans);
}
}
4. 最大收益问题
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param k int整型 表示你只能串行的最多做k个项目
* @param m int整型 表示你初始的资金
* @param costs int整型一维数组 costs[i]表示i号项目的花费
* @param profits int整型一维数组 profits[i]表示i号项目在扣除花费之后还能挣到的钱(利润)
* @return int整型
*/
public static class Project{
public int cost;
public int profit;
public Project(int c ,int p){
cost=c;
profit=p;
}
}
public static class Compare1 implements Comparator<Project>{//小根堆
public int compare(Project o1,Project o2) {
return o1.cost-o2.cost;
}
}
public static class Compare2 implements Comparator<Project>{//大根堆
public int compare(Project o1,Project o2) {
return o2.profit-o1.profit;
}
}
public int findMaximizedCapital (int k, int m, int[] costs, int[] profits) {
PriorityQueue<Project>smallRootHeap=new PriorityQueue<>(new Compare1());
PriorityQueue<Project>bigRootHeap=new PriorityQueue<>(new Compare2());
if(k<1)return m;
for(int i=0;i<costs.length;++i){
smallRootHeap.add(new Project(costs[i],profits[i]));
}
for(int i=0;i<k;++i){
while(!smallRootHeap.isEmpty()&&smallRootHeap.peek().cost<=m){
bigRootHeap.add(smallRootHeap.poll());
}
if(bigRootHeap.isEmpty()){
return m;
}
m+=bigRootHeap.poll().profit;
}
return m;
}
}
5. 三国游戏
贪心+博弈论
小涵一定会赢,因为他决定了计算机的抉择,只要小涵拿到每个武将与其他武将第二大匹配值中的最大的那一个就一定能赢 ,而小涵一定能拿到。
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;
//三国游戏
public class Main{
public static class Compare2 implements Comparator<Integer>{//大根堆
public int compare(Integer o1,Integer o2) {
return o2-o1;
}
}
public static void main(String[] args){
Scanner scan=new Scanner(System.in);
int N=scan.nextInt();
int[][] value=new int[N][N];
PriorityQueue<Integer>bigRootHeap=new PriorityQueue<>(new Compare2());
for(int i=0;i<N-1;++i) {
for(int j=i+1;j<N;++j) {
value[i][j]=scan.nextInt();
value[j][i]=value[i][j];
}
}
for(int i=0;i<N;++i) {
PriorityQueue<Integer>heap=new PriorityQueue<>(new Compare2());
for(int j=0;j<N;++j) {
heap.add(value[i][j]);
}
heap.poll();
bigRootHeap.add(heap.poll());
}
System.out.println(1+"\n"+bigRootHeap.poll());
}
}
6. 合并果子
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;
//合并果子
public class Main {
public static class Compare1 implements Comparator<Integer>{//小根堆
public int compare(Integer o1,Integer o2) {
return o1-o2;
}
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int N=scan.nextInt();
PriorityQueue<Integer>smallRootHeap=new PriorityQueue<>(new Compare1());
for(int i=0;i<N;++i) {
smallRootHeap.add(scan.nextInt());
}
int ans=0;
while(smallRootHeap.size()>1) {
int a=smallRootHeap.poll();
int b=smallRootHeap.poll();
ans+=(a+b);
smallRootHeap.add(a+b);
}
System.out.println(ans);
}
}
7. 独立的小易
import java.util.Scanner;
//独立的小易
public class Main {
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int x,f,d,p;//x--房租 f--水果数 d--手头现金 p--苹果单价
x=scan.nextInt();
f=scan.nextInt();
d=scan.nextInt();
p=scan.nextInt();
int day=0;
while(d>=x&&f>0||f==0&&d>=x+p) {
day++;
if(f>0) {
f--;
d-=x;
}
else {
d-=(p+x);
}
}
System.out.println(day);
}
}
8. 数轴覆盖
方法一:二分
import java.util.Scanner;
//数轴覆盖
public class Main {
public static int process(int[] arr,int k) {
int ans=0;
for(int i=0;i<arr.length;++i) {
int mostLeft=getIndex(arr,0,i,arr[i]-k);
ans=Math.max(ans, i-mostLeft+1);
}
return ans;
}
public static int getIndex(int[] arr,int l,int r,int target) {// >target最左的点
int mid=l+((r-l)>>2);
while(l<=r) {
if(arr[mid]>target) {
r=mid-1;
}else {
l=mid+1;
}
mid=l+((r-l)>>2);
}
return l;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n,k;
n=scan.nextInt();
k=scan.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;++i) {
arr[i]=scan.nextInt();
}
System.out.println(process(arr,k));
}
}
方法二:滑动窗口
import java.util.Scanner;
//数轴覆盖
public class Main {
public static int slideWindow(int[] arr,int k) {
int l=0,r=0;
int ans=0;
while(r<arr.length&&l<arr.length) {
while(r+1<arr.length&&arr[r+1]-arr[l]<k) {
r++;
}
ans=Math.max(ans, r-l+1);
l++;
}
return ans;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n,k;
n=scan.nextInt();
k=scan.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;++i) {
arr[i]=scan.nextInt();
}
System.out.println(slideWindow(arr,k));
}
}
9. 超级洗衣机
class Solution {
public int findMinMoves(int[] machines) {
int ans=0;
int sum=0;
for(int i=0;i<machines.length;++i){
sum+=machines[i];
}
int n=machines.length;
if(sum%n!=0)return -1;
int avg=sum/n;
int sumLeft=0;
int a=0,b=0,c=sum-sumLeft-machines[0],d=(n-1)*avg;
for(int i=0;i<n;++i){
if(a<b&&c<d){
ans=Math.max(ans,Math.abs(a-b)+Math.abs(c-d));
}else{
ans=Math.max(ans,Math.max(Math.abs(a-b),Math.abs(c-d)));
}
sumLeft+=machines[i];
a=sumLeft;
b=(i+1)*avg;
c=sum-sumLeft-(i+1<n?machines[i+1]:0);
d=(n-i-2)*avg;
}
return ans;
}
}
10. 容器装水
import java.util.Scanner;
public class Main{
public static long process(int[] arr) {
long ans=0;
long leftMax=arr[0];
long rightMax=arr[arr.length-1];
int L=1,R=arr.length-2;
while(L<=R) {
if(leftMax<=rightMax) {
long cur=(leftMax-arr[L]>0?leftMax-arr[L]:0);
leftMax=Math.max(leftMax, arr[L]);
L++;
ans+=cur;
}else {
long cur=(rightMax-arr[R]>0?rightMax-arr[R]:0);
rightMax=Math.max(rightMax, arr[R]);
R--;
ans+=cur;
}
}
return ans;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;++i) {
arr[i]=scan.nextInt();
}
System.out.println(process(arr));
}
}
11. 地形盛水
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;
public class Main {
public static class Node{
public int row;
public int col;
public int val;
public Node(int r,int c,int v) {
row=r;
col=c;
val=v;
}
}
public static class Compare implements Comparator<Node>{
public int compare(Node o1,Node o2) {
return o1.val-o2.val;
}
}
public static int process(int[][] arr,int n,int m) {
int ans=0;
boolean[][] isEnter=new boolean[n][m];
PriorityQueue<Node>smallRootHeap=new PriorityQueue<>(new Compare());
for(int col=0;col<m;++col) {
smallRootHeap.add(new Node(0,col,arr[0][col]));
// smallRootHeap.add(new Node(n-1,col,arr[0][col]));
isEnter[0][col]=true;
// isEnter[n-1][col]=true;
}
for(int row=1;row<n;++row) {
smallRootHeap.add(new Node(row,m-1,arr[row][m-1]));
isEnter[row][m-1]=true;
}
for(int col=m-2;col>=0;--col) {
smallRootHeap.add(new Node(n-1,col,arr[n-1][col]));
isEnter[n-1][col]=true;
}
for(int row=n-2;row>0;--row) {
smallRootHeap.add(new Node(row,0,arr[row][0]));
isEnter[row][0]=true;
}
int max=0;
while(!smallRootHeap.isEmpty()) {
Node cur=smallRootHeap.poll();
max=Math.max(max, cur.val);
if(cur.row-1>=0&&cur.row-1<n&&isEnter[cur.row-1][cur.col]==false) {//上
smallRootHeap.add(new Node(cur.row-1,cur.col,arr[cur.row-1][cur.col]));
isEnter[cur.row-1][cur.col]=true;
ans+=(max>arr[cur.row-1][cur.col]?max-arr[cur.row-1][cur.col]:0);
}
if(cur.row+1>=0&&cur.row+1<n&&isEnter[cur.row+1][cur.col]==false) {//下
smallRootHeap.add(new Node(cur.row+1,cur.col,arr[cur.row+1][cur.col]));
isEnter[cur.row+1][cur.col]=true;
ans+=(max>arr[cur.row+1][cur.col]?max-arr[cur.row+1][cur.col]:0);
}
if(cur.col-1>=0&&cur.col-1<m&&isEnter[cur.row][cur.col-1]==false) {//左
smallRootHeap.add(new Node(cur.row,cur.col-1,arr[cur.row][cur.col-1]));
isEnter[cur.row][cur.col-1]=true;
ans+=(max>arr[cur.row][cur.col-1]?max-arr[cur.row][cur.col-1]:0);
}
if(cur.col+1>=0&&cur.col+1<m&&isEnter[cur.row][cur.col+1]==false) {//右
smallRootHeap.add(new Node(cur.row,cur.col+1,arr[cur.row][cur.col+1]));
isEnter[cur.row][cur.col+1]=true;
ans+=(max>arr[cur.row][cur.col+1]?max-arr[cur.row][cur.col+1]:0);
}
}
return ans;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n,m;
n=scan.nextInt();
m=scan.nextInt();
int[][] arr=new int[n][m];
for(int i=0;i<n;++i) {
for(int j=0;j<m;++j) {
arr[i][j]=scan.nextInt();
}
}
System.out.println(process(arr,n,m));
}
}
12. 安置路灯
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
scan.nextLine();
String s=scan.nextLine();
int cur=0;
int ans=0;
while(cur<n) {
if(s.charAt(cur)=='X') {
cur++;
}else {
ans++;
if(cur+1<n&&s.charAt(cur+1)=='X') {
cur=cur+2;
}else if(cur+1<n&&s.charAt(cur+1)=='.') {
cur=cur+3;
}else {
cur++;
}
}
}
System.out.println(ans);
}
}
13. 我的世界
解题思路:
每个柱子都有一定的方块数,要想让柱子从左到右方块数一次增加,就需要从左到右,在这个柱子的方块数不少于前一个的前提下,使得这个柱子的方块数量尽可能地少。( 使左边柱子的方块数量尽可能少 )
首先左边第一个柱子,方块数越少越好,因此第一个柱子就直接进行一次操作
之后从第二个柱子开始向右遍历所有的柱子,遍历到这个柱子时,如果这个柱子方块儿仍比前一个少,就直接No(前面一个柱子的方块数已经达到最小值)。
否则,如果这个柱子的方块比上一个多,就可以减少一个(减少后仍不少于前一个柱子)
但如果这个柱子的方块数量和上一个已经一样多了,那么这个柱子就不能再减少方块数量了。
如果遍历到最后也没有输出No,就输出Yes即可。
import java.util.Scanner;
public class Main {
public static boolean process(int[] arr) {
if(arr[0]>0)arr[0]--;
for(int i=1;i<arr.length;++i) {
if(arr[i]==arr[i-1])continue;
else if(arr[i]<arr[i-1])return false;
else arr[i]--;
}
return true;
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
int n=scan.nextInt();
boolean ans;
int[] arr=new int[n];
scan.nextLine();
String[] nums=scan.nextLine().split(" ");
for(int i=0;i<n;++i) {
arr[i]=Integer.parseInt(nums[i]);
}
ans=process(arr);
if(ans) {
System.out.println("Yes");
}else {
System.out.println("No");
}
}
}