1317. 将整数转化为两个无零整数的和
「无零整数」是十进制表示中 不含任何 0 的正整数。
给你一个整数 n,请你返回一个 由两个整数组成的列表 [A, B],满足:
A 和 B 都是无零整数
A + B = n
题目数据保证至少有一个有效的解决方案。
如果存在多个有效解决方案,你可以返回其中任意一个。
示例 1:
输入:n = 2
输出:[1,1]
解释:A = 1, B = 1. A + B = n 并且 A 和 B 的十进制表示形式都不包含任何 0 。
示例 2:
输入:n = 11
输出:[2,9]
提示:
2 <= n <= 10^4
分析:
这道题比较水,纯暴力就可以AC。不过看了大家的解法,发现基本都是暴力解的。无非是在判断含“0”上做法不同。
//我的解法:
public int[] getNoZeroIntegers(int n) {
int[] result = new int[2];
for (int i = 1; i < n; i++) {
if (isNoZeroInteger(i) && isNoZeroInteger(n - i)) {
result[0] = i;
result[1] = n - i;
}
}
return result;
}
private boolean isNoZeroInteger(int value) {
while (value != 0) {
if (value % 10 == 0) {
return false;
}
value = value / 10;
}
return true;
}
//优化解法(通过字符串函数直接进行判断)
public int[] getNoZeroIntegers(int n) {
for (int i = 1; i < n; i++) {
int num = n - i;
if (!String.valueOf(i).contains("0")
&& !String.valueOf(num).contains("0")) {
return new int[]{i, num};
}
}
return null;
}
1318. 或运算的最小翻转次数
给你三个正整数 a、b 和 c。
你可以对 a 和 b 的二进制表示进行位翻转操作,返回能够使按位或运算 a OR b == c 成立的最小翻转次数。
「位翻转操作」是指将一个数的二进制表示任何单个位上的 1 变成 0 或者 0 变成 1 。
示例 1:
输入:a = 2, b = 6, c = 5
输出:3
解释:翻转后 a = 1 , b = 4 , c = 5 使得 a OR b == c
提示:
1 <= a <= 10^9
1 <= b <= 10^9
1 <= c <= 10^9
分析:
(1)先将a、b、c转化成二进制,再每次取a、b、c二进制的个位数字,记为va、vb、vc。
注意:最好采用位操作符实现,而不是通过数学运算。
a & 1:取个位数字。例如 a = 10011, 则 a & 1 = 10011 & 00001 = 1
a >> 1:右移一位。例如 a = 10101, 则 a >> 1 = 10011 >> 1 = 01001
(2)判断 va | vb == vc 是否成立:
1)成立,不用翻转;
2)不成立
i. vc == 1,此时只可能 va = vb = 0,需要翻转1次
ii. vc == 0,
* va != vb,需要翻转1次;
* va = vb = 1,需要翻转2次
public int minFlips(int a, int b, int c) {
int count = 0;
while (c > 0 || a > 0 || b > 0) {
int vc = c & 1;
int va = a & 1;
int vb = b & 1;
if (vc != (va | vb)) {
if (vc == 0) {
count += (va == vb ? 2 : 1);
} else {
count += 1;
}
}
c = c >> 1;
a = a >> 1;
b = b >> 1;
}
return count;
}
1319. 连通网络的操作次数
用以太网线缆将 n 台计算机连接成一个网络,计算机的编号从 0 到 n-1。线缆用 connections 表示,其中 connections[i] = [a, b] 连接了计算机 a 和 b。
网络中的任何一台计算机都可以通过网络直接或者间接访问同一个网络中其他任意一台计算机。
给你这个计算机网络的初始布线 connections,你可以拔开任意两台直连计算机之间的线缆,并用它连接一对未直连的计算机。请你计算并返回使所有计算机都连通所需的最少操作次数。如果不可能,则返回 -1 。
示例 1:
输入:n = 4, connections = [[0,1],[0,2],[1,2]] 输出:1 解释:拔下计算机 1 和 2 之间的线缆,并将它插到计算机 1 和 3 上 提示: 1 <= n <= 10^5 1 <= connections.length <= min(n*(n-1)/2, 10^5) connections[i].length == 2 0 <= connections[i][0], connections[i][1] < n connections[i][0] != connections[i][1] 没有重复的连接。 两台计算机不会通过多条线缆连接。
分析:
看到“连通”首先想到的就是并查集,这道题是典型的并查集问题。
将互相连通的电脑压缩成一个点,若最后存在 N 个点,通过观察可以发现只需要 N - 1 条线就可以进行相连。
那接下来的问题就是如何去寻找这 N - 1 条线。
考虑每一条线,如果线的两头已经连通(find(p) == find(q)),则这条线是多余的,可以拿去当做 N - 1 条线的其中一条。
class Solution {
int[] father;
int[] sz;
int num;
public int find(int p) {
if (p != father[p]) {
p = find(father[p]);
}
return p;
}
public void union(int p, int q) {
int i = find(p);
int j = find(q);
if (i == j) return;
num -= 1;
if (sz[i] < sz[j]) {
father[i] = j;
sz[j] += sz[i];
} else {
father[j] = i;
sz[i] += sz[j];
}
}
public void initUF(int n) {
father = new int[n];
sz = new int[n];
num = n;
for (int i = 0; i < n; i++) {
father[i] = i;
sz[i] = 1;
}
}
public int makeConnected(int n, int[][] connections) {
initUF(n);
// 多余的线缆数量
int cnt = 0;
for (int[] c : connections) {
int f = c[0], t = c[1];
// 两个点已经连通,不需要这个线缆
if (find(f) == find(t)) {
cnt += 1;
continue;
}
union(f, t);
}
// 所需要的线缆数量
int cnt2 = num - 1;
if (cnt < cnt2) {
return -1;
}
return cnt2;
}
}
1320. 二指输入的最小距离
二指输入法定制键盘在 XY 平面上的布局如上图所示,其中每个大写英文字母都位于某个坐标处,例如字母 A 位于坐标 (0,0),字母 B 位于坐标 (0,1),字母 P 位于坐标 (2,3) 且字母 Z 位于坐标 (4,1)。
给你一个待输入字符串 word,请你计算并返回在仅使用两根手指的情况下,键入该字符串需要的最小移动总距离。坐标 (x1,y1) 和 (x2,y2) 之间的距离是 |x1 - x2| + |y1 - y2|。
注意,两根手指的起始位置是零代价的,不计入移动总距离。你的两根手指的起始位置也不必从首字母或者前两个字母开始。
示例:
输入:word = "CAKE"
输出:3
解释:
使用两根手指输入 "CAKE" 的最佳方案之一是:
手指 1 在字母 'C' 上 -> 移动距离 = 0
手指 1 在字母 'A' 上 -> 移动距离 = 从字母 'C' 到字母 'A' 的距离 = 2
手指 2 在字母 'K' 上 -> 移动距离 = 0
手指 2 在字母 'E' 上 -> 移动距离 = 从字母 'K' 到字母 'E' 的距离 = 1
总距离 = 3
提示:
2 <= word.length <= 300
- 每个
word[i]
都是一个大写英文字母。
分析:
典型的动态规划问题。我们需要一个三维的状态来表示整个动态规划的过程,包括当前考虑的字母下标,左指的键位,右指的键位。
假设字符串为 CAKE,并且此时阶段为 1,即当前考虑字母是 A。在这个阶段下,左右指会存在一种现象,要么左指为 A ,要么右指为 A,此时才能输入字母 A。
对于左指为 A,表示我们通过移动左指来到达这个阶段,而右指是没有移动的。总结来说,这个阶段下,左指会变成 A,右指不变。因此,我们需要遍历上一个阶段左指和右指的所有情况,并且转移到下一个阶段时,只移动左指(dp[1][A][R] = Math.min(dp[1][A][R], dp[0][L][R] + move(L, A)))。
对于右指为 A
的情况同理。
class Solution {
public int minimumDistance(String word) {
// 初始化
int[][][] dp = new int[301][26][26];
for (int i = 1; i <= 300; i++) {
for (int j = 0; j < 26; j++) {
Arrays.fill(dp[i][j], Integer.MAX_VALUE);
}
}
int ans = Integer.MAX_VALUE;
char[] ca = word.toCharArray();
// 遍历每个字母
for (int i = 1; i <= word.length(); i++) {
int v = ca[i - 1] - 'A';
// 遍历上一个阶段左指键位
for (int l = 0; l < 26; l++) {
// 遍历上一个阶段右指键位
for (int r = 0; r < 26; r++) {
// 判断上一个阶段的状态是否存在
if (dp[i - 1][l][r] != Integer.MAX_VALUE) {
// 移动左指
dp[i][v][r] = Math.min(dp[i][v][r], dp[i - 1][l][r] + help(l, v));
// 移动右指
dp[i][l][v] = Math.min(dp[i][l][v], dp[i - 1][l][r] + help(r, v));
}
if (i == word.length()) {
ans = Math.min(ans, dp[i][v][r]);
ans = Math.min(ans, dp[i][l][v]);
}
}
}
}
return ans;
}
// 计算距离
public int help(int a, int b) {
int x = a / 6, y = a % 6;
int x2 = b / 6, y2 = b % 6;
return (int)(Math.abs(x - x2)) + (int)(Math.abs(y - y2));
}
}