LeetCode Weekly Contest 133题解

1029. 两地调度

公司计划面试 2N 人。第 i 人飞往 A 市的费用为 costs[i][0],飞往 B 市的费用为 costs[i][1]

返回将每个人都飞到某座城市的最低费用,要求每个城市都有 N 人抵达

 

示例:

输入:[[10,20],[30,200],[400,50],[30,20]]
输出:110
解释:
第一个人去 A 市,费用为 10。
第二个人去 A 市,费用为 30。
第三个人去 B 市,费用为 50。
第四个人去 B 市,费用为 20。

最低总费用为 10 + 30 + 50 + 20 = 110,每个城市都有一半的人在面试。

 

提示:

  1. 1 <= costs.length <= 100
  2. costs.length 为偶数
  3. 1 <= costs[i][0], costs[i][1] <= 1000

思路1:

  • 贪心
  • 先算出一个人去A市和B市的一个差值的绝对值,然后按绝对值进行排序,要使最后的费用总值最小,那么你需要先从差值较大的人中选出费用较小的,若去A市(B市)的人数够了,那么剩下的都去B市(A市)不需要再理他们之间的差值,因为你之后即使被迫选了两地费用中较大的,所造成的结果也没有前面差值较大中选的较大的值所造成的费用大

AcCode:

class Solution {
    private static class Job implements Comparable<Job>{
		int freeA;
		int freeB;
		int gz;
		
		public Job(int freeA,int freeB) {
			this.freeA = freeA;
			this.freeB = freeB;
			this.gz = Math.abs(freeA-freeB);
		}

		@Override
		public int compareTo(Job o) {
			return gz-o.gz;
		}
	}
    public int twoCitySchedCost(int[][] costs) {
        
		int N = costs.length/2;
		Job[] jobs = new Job[costs.length];
		for (int i = 0; i < costs.length; i++) {
			jobs[i] = new Job(costs[i][0], costs[i][1]);
		}
		Arrays.sort(jobs);
		int res = 0;
		int A = 0;//A地数量
		int B = 0;//B地数量
		for (int i = jobs.length-1; i >=0; i--) {
			if(B==N) {
				res+=jobs[i].freeA;
				continue;
			}else if(A==N) {
				res+=jobs[i].freeB;
				continue;
			}
			
			if(jobs[i].freeA>jobs[i].freeB) {
				res+=jobs[i].freeB;
				B++;
			}else {
				res+=jobs[i].freeA;
				A++;
			}
		}
		
		return res;
    
    }
}

思路2:

  • dp
  • 我们发现有i个人,若j个人去A地,那么就有(i-j)个人去B地
  • i表示总共的人数,j表示去A地的人数
  • 若j=0,那么代表没有人去A地,那么都只能去B地,所以dp[i][0] = dp[i-1][0]+costs[i-1][1](i>0)
  • 设cost_a = costs[i-1][0],cost_b = costs[i-1][1],dp[i][j] = Math.min(dp[i-1][j-1]+cost_a,dp[i-1][j]+cost_b)
  • 这里需要以下特判i==j时,该人只能去A地不能去B地,因为之前没有足够的人去A地

                                  (dp图片来自花花酱,侵删)

AcCode:

class Solution {
    public int twoCitySchedCost(int[][] costs) {
		int n = costs.length;
		int[][] dp = new int[n+1][n+1];
		//初始化
		for (int i = 1; i < dp.length; i++) {
			dp[i][0] = dp[i-1][0]+costs[i-1][1];
		}
		
		for (int i = 1; i < dp.length; i++) {
			for (int j = 1; j<i && j < dp[i].length; j++) {
				//min(去B地,去A地);
				dp[i][j] = Math.min(dp[i-1][j]+costs[i-1][1], dp[i-1][j-1]+costs[i-1][0]);
			}
			dp[i][i] = dp[i-1][i-1]+costs[i-1][0];
		}
        return dp[n][n/2];
    
    }
}

1030. 距离顺序排列矩阵单元格

给出 R 行 C 列的矩阵,其中的单元格的整数坐标为 (r, c),满足 0 <= r < R 且 0 <= c < C

另外,我们在该矩阵中给出了一个坐标为 (r0, c0) 的单元格。

返回矩阵中的所有单元格的坐标,并按到 (r0, c0) 的距离从最小到最大的顺序排,其中,两单元格(r1, c1) 和 (r2, c2) 之间的距离是曼哈顿距离,|r1 - r2| + |c1 - c2|。(你可以按任何满足此条件的顺序返回答案。)

 

示例 1:

输入:R = 1, C = 2, r0 = 0, c0 = 0
输出:[[0,0],[0,1]]
解释:从 (r0, c0) 到其他单元格的距离为:[0,1]

示例 2:

输入:R = 2, C = 2, r0 = 0, c0 = 1
输出:[[0,1],[0,0],[1,1],[1,0]]
解释:从 (r0, c0) 到其他单元格的距离为:[0,1,1,2]
[[0,1],[1,1],[0,0],[1,0]] 也会被视作正确答案。

示例 3:

输入:R = 2, C = 3, r0 = 1, c0 = 2
输出:[[1,2],[0,2],[1,1],[0,1],[1,0],[0,0]]
解释:从 (r0, c0) 到其他单元格的距离为:[0,1,1,2,2,3]
其他满足题目要求的答案也会被视为正确,例如 [[1,2],[1,1],[0,2],[1,0],[0,1],[0,0]]。

 

提示:

  1. 1 <= R <= 100
  2. 1 <= C <= 100
  3. 0 <= r0 < R
  4. 0 <= c0 < C

 思路:

  • 枚举每一个点,计算出每一个点与给定点之间的曼哈顿距离,之后排序就行

AcCode:

class Solution {
    private static class Point implements Comparable<Point>{
		int x;
		int y;
		int jl;
		
		public Point(int x,int y,int jl) {
			this.x = x;
			this.y = y;
			this.jl = jl;
		}

		@Override
		public int compareTo(Point o) {
			return jl-o.jl;
		}
		
		
	}
    public int[][] allCellsDistOrder(int R, int C, int r0, int c0) {
        
		int[][] res = new int[R*C][2];
		Point[] points = new Point[R*C];
		int index = 0;
		for (int i = 0; i < R; i++) {
			for (int j = 0; j < C; j++) {
				points[index++] = new Point(i, j, Math.abs(i-r0)+Math.abs(j-c0));
			}
		}
		Arrays.sort(points);
		for (int i = 0; i < points.length; i++) {
			res[i][0] = points[i].x;
			res[i][1] = points[i].y;
		}
        return res;
    
    }
}

 


1031. 两个非重叠子数组的最大和

给出非负整数数组 A ,返回两个非重叠(连续)子数组中元素的最大和,子数组的长度分别为 L 和 M。(这里需要澄清的是,长为 L 的子数组可以出现在长为 M 的子数组之前或之后。)

从形式上看,返回最大的 V,而 V = (A[i] + A[i+1] + ... + A[i+L-1]) + (A[j] + A[j+1] + ... + A[j+M-1]) 并满足下列条件之一:

 

  • 0 <= i < i + L - 1 < j < j + M - 1 < A.length
  • 0 <= j < j + M - 1 < i < i + L - 1 < A.length.

 

示例 1:

输入:A = [0,6,5,2,2,5,1,9,4], L = 1, M = 2
输出:20
解释:子数组的一种选择中,[9] 长度为 1,[6,5] 长度为 2。

示例 2:

输入:A = [3,8,1,3,2,1,8,9,0], L = 3, M = 2
输出:29
解释:子数组的一种选择中,[3,8,1] 长度为 3,[8,9] 长度为 2。

示例 3:

输入:A = [2,1,5,6,0,9,5,0,3,8], L = 4, M = 3
输出:31
解释:子数组的一种选择中,[5,6,0,9] 长度为 4,[0,3,8] 长度为 3。

 

提示:

  1. L >= 1
  2. M >= 1
  3. L + M <= A.length <= 1000
  4. 0 <= A[i] <= 1000

思路:

  • 枚举由L构成的子数组,记录构成子数组的beginIndex,endIndex,Value,生成listL,同理枚举M构成的子数组生成listM
  • 遍历listL和listM,若两个子数组不相交,那么计算两个子数组的和,从中取出最大值

AcCode:

class Solution {
    private static class Node{
		int begin;
		int end;
		int value;
		
		public Node(int begin,int end,int value) {
			this.begin = begin;
			this.end = end;
			this.value = value;
		}
	}
    public int maxSumTwoNoOverlap(int[] A, int L, int M) {
        
		List<Node> listL = new ArrayList<Solution.Node>();
		int beginLValue = 0;
		for (int i = 0; i < L; i++) {
			beginLValue+=A[i];
		}
		listL.add(new Node(0, L-1, beginLValue));
		for (int i = L; i < A.length; i++) {
			beginLValue = beginLValue+A[i]-A[i-L];
			listL.add(new Node(i-L+1, i, beginLValue));
		}
		
		List<Node> listM = new ArrayList<Solution.Node>();
		int beginMValue = 0;
		for (int i = 0; i < M; i++) {
			beginMValue+=A[i];
		}
		listM.add(new Node(0, M-1, beginMValue));
		for (int i = M; i < A.length; i++) {
			beginMValue = beginMValue+A[i]-A[i-M];
			listM.add(new Node(i-M+1, i, beginMValue));
		}
		
		int maxValue = 0;
		for (int i = 0; i < listL.size(); i++) {
			for (int j = 0; j < listM.size(); j++) {
				if(listL.get(i).end<listM.get(j).begin || listM.get(j).end<listL.get(i).begin) {
					int k = listL.get(i).value+listM.get(j).value;
					if(k>maxValue) {
						maxValue = k;
					}
				}
			}
		}
        return maxValue;
    
    }
}

1032. 字符流

按下述要求实现 StreamChecker 类:

  • StreamChecker(words):构造函数,用给定的字词初始化数据结构。
  • query(letter):如果存在某些 k >= 1,可以用查询的最后 k个字符(按从旧到新顺序,包括刚刚查询的字母)拼写出给定字词表中的某一字词时,返回 true。否则,返回 false

 

示例:

StreamChecker streamChecker = new StreamChecker(["cd","f","kl"]); // 初始化字典
streamChecker.query('a');          // 返回 false
streamChecker.query('b');          // 返回 false
streamChecker.query('c');          // 返回 false
streamChecker.query('d');          // 返回 true,因为 'cd' 在字词表中
streamChecker.query('e');          // 返回 false
streamChecker.query('f');          // 返回 true,因为 'f' 在字词表中
streamChecker.query('g');          // 返回 false
streamChecker.query('h');          // 返回 false
streamChecker.query('i');          // 返回 false
streamChecker.query('j');          // 返回 false
streamChecker.query('k');          // 返回 false
streamChecker.query('l');          // 返回 true,因为 'kl' 在字词表中。

 

提示:

  • 1 <= words.length <= 2000
  • 1 <= words[i].length <= 2000
  • 字词只包含小写英文字母。
  • 待查项只包含小写英文字母。
  • 待查项最多 40000 个。

 思路:

  • 将给定的单词逆序构成字典树,之后将query构成的单词逆序查找字典树有没有该单词就行

AcCode:

class StreamChecker {
  
		
		class TireNode{
			int isEnd = 0;
			TireNode[] tireNodes = new TireNode[26];
			
			public TireNode() {
				
			}
		}
		
		TireNode root = null;//字典树根结点
		
		public void insert(String s) {
			TireNode p = root;
			for (int i = s.length()-1; i >0; i--) {
				char target = s.charAt(i);
				if(p.tireNodes[target-'a']==null) {
					p.tireNodes[target-'a'] = new TireNode();
				}
				p = p.tireNodes[target-'a'];
			}
			char target = s.charAt(0);
			if(p.tireNodes[target-'a']==null) {
				p.tireNodes[target-'a'] = new TireNode();
			}
			p.tireNodes[target-'a'].isEnd = 1;
		}
		
	    public StreamChecker(String[] words) {
	    	root = new TireNode();
	    	//建立一个字典树
	    	for (int i = 0; i < words.length; i++) {
				insert(words[i]);
			}
	    }
	    
	    StringBuilder builder = new StringBuilder();
	    public boolean query(char letter) {
	    	builder.append(letter);
	    	TireNode p = root;
	    	for (int i = builder.length()-1; i >=0; i--) {
				char target = builder.charAt(i);
				if(p.tireNodes[target-'a']==null) {
					return false;
				}else if(p.tireNodes[target-'a'].isEnd==1) {
					return true;
				}
				p = p.tireNodes[target-'a'];
			}
	        return false;
	    }
	
}

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值