2020/08/23 第33场双周赛和第203场周赛
1558、得到目标数的操作数
这道题在比赛时候把我关了。没有很敏锐的意识到涉及到2的次方的操作应该首先想到二进制的方法,这道题目很巧妙的计算出来了每个数字变为0所需要的次数。最终我们的答案应该是两部分的和,一个是对每一位数字进行修饰,修饰的表示就是保证每一位数字的二进制只有最高位是1,其余的都是0。第二部分就是计算数组中全部数组数字变为二进制之后最长的。
Java操作注意(num>>i & 1) == 1,这里的括号是不可省略的。
class Solution {
public int minOperations(int[] nums) {
int x = 0;
int k = 0;
int ans = 0;
for (int num:nums){
for (int i = 0; (num>>i) > 0; i++,x++ ){
if (((num>>i) & 1) == 1) ans++;
}
k = Math.max(k,x-1); // 因为是最后才++x
x = 0;
}
return Math.max(k,0)+ans;
}
}
1559、二维网格探测环
非常好的一个题目,可以使用并查集,可以使用bfs可以使用dfs。
首先使用比较熟练的bfs方法,需要注意一个细节是不能与之前的节点重复。因此需要另外维护一个队列保存父节点。
class Solution {
public boolean containsCycle(char[][] grid) {
int n = grid.length;
int m = grid[0].length;
int[][] vis = new int[n][m];
Queue<int[]> queue = new LinkedList<>();
Queue<int[]> pq = new LinkedList<>();
int[][] dis = new int[][]{{0,1}, {1,0}, {-1,0}, {0,-1}};
for (int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if (vis[i][j] == 0){
char word = grid[i][j];
queue.offer(new int[] {i,j});
pq.offer(new int[] {-1,-1});
while (!queue.isEmpty()){
int[] cur = queue.poll();
int[] p = pq.poll();
for (int[] d:dis){
int cur_x = cur[0]+d[0];
int cur_y = cur[1]+d[1];
if (cur_y == p[1] && cur_x == p[0]) continue;
if (0<=cur_x && cur_x<n && 0<=cur_y && cur_y<m && grid[cur_x][cur_y] == word){
if (vis[cur_x][cur_y] == 1) return true;
else {
queue.offer(new int[]{cur_x,cur_y});
pq.offer(cur);
vis[cur_x][cur_y] = 1;
}
}
}
}
}
}
}
return false;
}
}
另外学习一下并查集的写法。
class UF {
private int[] fa;
private int[] sz;
public UF(int N){
fa = new int[N];
for (int i = 0; i < N; i++) fa[i] = i;
sz = new int[N];
for (int i = 0; i<N; i++)sz[i] = 1;
}
public int findfa(int p){
if (fa[p] == p) return p; // 如果是根节点就返回本身
fa[p] = findfa(fa[p]);
return fa[p];
// 否则返回父节点的跟
}
public boolean isUnion(int a, int b){
return findfa(a) == findfa(b);
}
public void union(int a, int b){
int afa = findfa(a);
int bfa = findfa(b);
if (sz[afa]>=sz[bfa]){
fa[bfa] = afa;
sz[afa] += sz[bfa];
}
else{
fa[afa] = bfa;
sz[bfa] += sz[afa];
}
}
}
class Solution {
// 并查集的方法
public boolean containsCycle(char[][] grid) {
int n = grid.length;
int m = grid[0].length;
UF uf = new UF(m*n);
for (int i = 0; i<n; i++){
for (int j = 0; j<m ;j++){
if (i>0 && grid[i][j] == grid[i-1][j]){
if (uf.isUnion(i*m+j, (i-1)*m+j))return true;
else uf.union(i*m+j, (i-1)*m+j);
}
if (j>0 && grid[i][j] == grid[i][j-1]){
if (uf.isUnion(i*m+j, i*m+j-1))return true;
else uf.union(i*m+j, i*m+j-1);
}
}
}
return false;
}
}
1564.查找区间M
这道题如果正向思考不是很好考虑,因为我们需要的是最后一个满足要求的解,因此我们这里倒着考虑,从最后一次(其实是倒数第二次开始考虑)。我们考虑了一个新的数据结构,Treeset
这是一个有序的集合。我们可以每次找到集合中大于某个数字的最小数字或者小于某个数字的最大值。
这样我们可以找到,当前位置为0,前一个0的位置,和后一个0的位置。如果出现了满足长度的解,就是我们所需要的。
TreeSet<Integer> set = new TreeSet<>();
生成一个Treesetint a = set.lower(index);
寻找到小于index的最大值int b = set.upper(index);
寻找大于index的最小值int a = set.floor(index);
寻找到小于等于index的最大值int b = set.ceiling(index);
寻找大于等于index的最小值
//import java.util.TreeSet;
class Solution {
public int findLatestStep(int[] arr, int m) {
TreeSet<Integer> set=new TreeSet<>();
set.add(0);
set.add(arr.length+1);
if(arr.length==m) return arr.length;
int n=arr.length;
for (int i = n-1; i >=0; i--) {
int index=arr[i];
分别找到左右两个最近的0的位置
int a=set.lower(index);
int b=set.higher(index);
if(index-a-1==m||b-index-1==m){
return i;
}
set.add(index);
}
return -1;
}
}
1563.石子游戏IV
思路还是区间dp, dp[i][j]
表示区间【i, j】之间的最大数值。这次在代码实现的角度我们使用了先外层循环区间长度,中间层是起点,最后枚举中间切断点。每次实际的有效得分是,当前的可以得到的得分min(ls, rs)
和对应的区间的潜力。
class Solution {
public int stoneGameV(int[] stoneValue) {
// 区间dp问题,决定采用三重循环,其中最外层是间隔,中间是起点,最后是分界点
int n = stoneValue.length;
int[][] dp = new int[n][n];
int[] presum = new int[n];
presum[0] = stoneValue[0];
for (int i = 1; i < n ; i++){
presum[i] = presum[i-1]+stoneValue[i];
}
for (int len = 2; len<=n; len++){ // 区间长度【2, n】
for (int i = 0; i+len<=n; i++){
int j = i+len-1; // 左端点j, 右端点k
for (int m = i; m<j; m++){
int l = dp[i][m];
int r = dp[m+1][j];
int ls = presum[m]-(i>0?presum[i-1]:0);
int rs = presum[j]-presum[m];
if (rs == ls){
dp[i][j] = Math.max(dp[i][j], ls+Math.max(r,l));
}
else{
if (rs<ls){
dp[i][j] = Math.max(dp[i][j], rs+r);
}
else{
dp[i][j] = Math.max(dp[i][j], ls+l);
}
}
}
}
}
return dp[0][n-1];
}
}