解题思路:直接遍历判断即可
func checkOnesSegment(s string) bool {
cnt, one, n := 0, 0, len(s)
for i := 0; i < n; i++ {
if s[i] == '0' {
if one > 0 {
cnt++
}
one =0
} else {
one++
}
}
if one > 0 {
cnt++
}
return cnt < 2
}
解题思路:首先计算数组和与目标值差多少 diff,如果 diff < 0,则挑 -limit 去填充,如果 diff > 0,则挑 limit 去填充。
func absInt(a int) int {
if a < 0 {
return -a
}
return a
}
func minElements(nums []int, limit int, goal int) int {
n, s := len(nums), 0
for i := 0; i < n; i++ {
s += nums[i]
}
diff := absInt(goal - s)
ret := diff / limit
if diff % limit > 0 {
ret++
}
return ret
}
解题思路
- 带最短路径限制的图(u → v 存在边,且 d[u] < d[v]),我们很容易知道其是一个 DAG,有着良好的拓扑序。
- 求解 DAG 中某个顶点 u → v 的路径数,我们直接采用 DP 求解即可。
type Edge struct {
v, w int
}
func countRestrictedPaths(n int, edges [][]int) int {
m := len(edges)
mod := int(1e9+7)
adj := make([][]Edge, n+1)
for i := 0; i < m; i++ {
u, v, w := edges[i][0], edges[i][1], edges[i][2]
adj[u] = append(adj[u], Edge{v, w})
adj[v] = append(adj[v], Edge{u, w})
}
// 最短路径求解采用 SPFA 算法,比赛时候写错了一个地方,导致 debug 了较长时间
// 模板算法一定要熟且尽可能手敲出来
dis := make([]int, n+1)
for i := 1; i <= n; i++ {
dis[i] = 0x3f3f3f3f3f3f3f3f
}
que := make([]int, 0, 100000)
inq := make([]bool, n+1)
que = append(que, n)
dis[n] = 0
inq[n] = true
for len(que) > 0 {
u := que[0]
que = que[1:]
inq[u] = false
for _, e := range adj[u] {
v, w := e.v, e.w
if dis[v] > dis[u] + w {
dis[v] = dis[u] + w
if !inq[v] {
inq[v] = true
que = append(que, v)
}
}
}
}
dp := make([]int, n+1)
mark := make([]bool, n+1)
ee := make([]Edge, 0, n)
for i := 1; i <= n; i++ {
ee = append(ee, Edge{i, dis[i]})
}
sort.Slice(ee, func(i, j int) bool {
return ee[i].w < ee[j].w
})
dp[n] = 1
mark[n] = true
for i := 1; i < n; i++ {
u := ee[i].v
for _, e := range adj[u] {
v := e.v
if mark[v] && dis[v] < dis[u] {
dp[u] = (dp[u] + dp[v]) % mod
}
}
mark[u] = true
if u == 1 {
return dp[u]
}
}
return 0
}
解题思路
- 虽然在解第三题的过程中也再想第四题,感觉是用 dp 做,但是在设计 dp 状态时,发现无论怎么设计都无法满足 dp 无后效性这一原则。其实本质上是没有挖掘出数组的性质来,导致无法设计正确的 dp。
- 首先由题目可知最终结果中需要满足 a1^a2^…a^k = a2^a3^…^ak^a(k+1)=a3^…,根据成立的等式,我们发现整个结果数组是存在长度为 k 的循环节的,即最终结果中 a1 = a(k+1) = a(2k+1) …, ak = a(2k) = a(3*k) …
- 由于存在长度为 k 的循环节,因此我们只需要确定 a1, a2, …, ak 最终是啥整个数组遍确定了;我们 dp 便可以按照这 k 来划分阶段。假设 dp(i, j) 表示前 i 组数确定后最终异或和为 j 情况下需要变更的最少元素数。我们在选择第 i 组数是啥的时候存在两种情况:
- 第 i 组数不属于原先 i 组中的任何一个数字,此时花费 size(i) 即第 i 组数的个数,在选择从 i - 1 阶段哪个状态转换而来时,我们只需要选择 dp(i-1,x) 最小值即可。
- 从 i 组数中选择一个数作为结果,求解花费数。其中一种情况 2 会对 1 的结果进行纠正(1 算大了)。
- 状态转移方程
- dp(i, j) = min{dp(i-1,k)}{0<=k<lim} + size(i)
- dp(i, j) = min{dp(i-1,j^x) + size(i)-cnt(x)},其中 x 为第i组中的某个数
- 上述 lim 即为 2^x > max{nums[i]},因为二进制异或中比最大值最高位的 1 还大的数字是没有意义的,只会引入更多的花费。想想这是为什么?
- 本题其实的通用问法是:最终所有长度为 k 子数组的异或和为 x。感觉这道题目应该在哪儿出现过。。。
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
func minInt(a, b int) int {
if a < b {
return a
}
return b
}
func minChanges(nums []int, k int) int {
n := len(nums)
group := make(map[int]map[int]int, k)
groupSize := make([]int, k+1)
for i := 1; i <= k; i++ {
group[i] = make(map[int]int)
}
maxV := 0
for i := 0; i < n; i++ {
idx := i % k
group[idx+1][nums[i]]++
groupSize[idx+1]++
maxV = maxInt(maxV, nums[i])
}
lim := 1
for lim <= maxV {
lim <<= 1
}
dp := make([][]int, k+1)
for i := 0; i <= k; i++ {
dp[i] = make([]int, lim)
for j := 0; j < lim; j++ {
dp[i][j] = 0x3f3f3f3f
}
}
dp[0][0] = 0
for i := 1; i <= k; i++ {
minV := dp[i-1][0]
for j := 1; j < lim; j++ {
minV = minInt(minV, dp[i-1][j])
}
for j := 0; j < lim; j++ {
dp[i][j] = minV + groupSize[i]
for x, c := range group[i] {
dp[i][j] = minInt(dp[i][j], dp[i-1][j^x] + groupSize[i]-c)
}
}
}
return dp[k][0]
}