为了更好的阅读体检,为了更好的阅读体检,,可以查看我的算法学习博客第二题-限行
在线评测链接:P1267
题目内容
塔子哥是一个富有的商人,他在一家大型贸易公司工作。他的公司位于城市的中心区,是一座高耸的摩天大楼。他住在郊区的别墅,是一座豪华的欧式建筑。他每天都要开车上下班,穿越城市的繁华和拥堵。
不幸的是,这个城市为了缓解交通拥堵,实行了限行规则,每天都有一些车牌号的最后一位数字被禁止上路。
塔子哥不想因为限行而迟到或者请假,因为他的工作很重要,涉及到很多国际贸易的合同和谈判。所以他想买几辆车,让他每天都有车可以用。
假设他不能换车牌号,也不能选择其他交通方式,而且他的工作时间是固定的,问他至少需要买多少辆车?如果没有办法做到,就输出 −1 。
输入描述
输入一共有 7 行,表示周一至周日的限行情况。
输入每一行,第 i 行的第一个数字 c_i 表示当天限行数字个数,随后输入个互不相同的数字,第 j 个数字为 ,表示限行数字。
对于所有的数据, 。
输出描述
输出为一个整数,表示塔子哥需要的最少车辆数或塔子哥不能保证每天都至少有一辆车可以出行。
样例
输入
8 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 8 2 3 4 5 6 7 8 9 8 0 1 2 3 4 5 8 9 8 0 1 2 3 6 7 8 9 8 0 1 4 5 6 7 8 9 8 2 3 4 5 6 7 8 9
输出
5
样例解释
一种可能的方案是,选购最后车牌一位数字分别为 0,2,4,6,8 的 5 辆车,此时每天都有车辆可以出行。
思路
二进制状态压缩
将车牌 0 到 9 分别看成 到 ,即二进制表示十进制数的存在与否。如此 [0, 1023] 就可以表示 0 到 9 是否存在了。
表示第 i 天允许通行的车牌号。
所以只要枚举 [1, 1023] 这些车牌号的状态表示,然后对满足任意一天都可以有车牌通行的所有车牌状态表示,计算这些状态表示的车牌的数量,并取个,每辆车都有不同的车牌号,故这就是最少的车牌数量就是需要的车的数量。
时间复杂度:
视频实况 v1, 08:18-21:06
类似题目推荐
LeetCode
-
78. 子集
-
2151. 基于陈述统计最多好人数
-
1601. 最多可达成的换楼请求数目
-
2002. 两个回文子序列长度的最大乘积
CodeFun2000
P1188 2023.04.12-微众银行春招-第三题-魔法收纳器 - 难度较大
代码
CPP
#include <bits/stdc++.h> using namespace std; int bit[10]; int c[7]; int main() { for (int i = 0; i < 7; ++i) { scanf("%d", &c[i]); for (int j = 0, x; j < c[i]; ++j) { // 先将第 i 天限行的每个尾号用二进制记录下来 scanf("%d", &x); bit[i] |= 1 << x; } // 通过 1111 1111 11 这个二进制表示全部尾号,减去限行的尾号,就是第 i 天不限行的尾号表示 bit[i] = (1 << 10) - 1 - bit[i]; } int ans = -1; // 枚举所有可能拥有的尾号表示 for (int i = 1; i < 1 << 10; ++i) { int cnt = 0; for (int j = 0; j < 10; ++j) { if (i >> j & 1) cnt += 1; } // 判断拥有的尾号与 7 天每天的不限行尾号是否有交集 // 如果都有交集就说明这个尾号表示是合法的 // 如果存在至少 1 天与这个尾号表示无交集,就说明这个尾号表示是不合法的 bool ok = true; for (int j = 0; j < 7; ++j) if ((i & bit[j]) == 0) { ok = false; break; } // 如果合法,取最少的尾号数量 if (ok) { if (ans == -1) ans = cnt; else ans = min(ans, cnt); } } printf("%d\n", ans); return 0; }
python
bit = [0] * 7 for i in range(7): lst = list(map(int, input().split())) for j in range(1, len(lst)): # 先将第 i 天限行的每个尾号用二进制记录下来 bit[i] |= 1 << lst[j] # 通过 1111 1111 11 这个二进制表示全部尾号,减去限行的尾号,就是第 i 天不限行的尾号表示 bit[i] = 1023 - bit[i] ans = -1 # 枚举所有可能拥有的尾号表示 for i in range(1, 1024): cnt = 0 for j in range(10): if i >> j & 1: cnt += 1 # 判断拥有的尾号与 7 天每天的不限行尾号是否有交集 # 如果都有交集就说明这个尾号表示是合法的 # 如果存在至少 1 天与这个尾号表示无交集,就说明这个尾号表示是不合法的 ok = True for j in range(7): if (bit[j] & i) == 0: ok = False break # 如果合法,取最少的尾号数量 if ok: if ans == -1: ans = cnt else: ans = min(ans, cnt) print(ans)
Java
import java.util.Scanner; class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int[] bit = new int[7]; for (int i = 0; i < 7; ++i) { int c = sc.nextInt(); for (int j = 0; j < c; ++j) { int x = sc.nextInt(); // 先将第 i 天限行的每个尾号用二进制记录下来 bit[i] |= 1 << x; } // 通过 1111 1111 11 这个二进制表示全部尾号,减去限行的尾号,就是第 i 天不限行的尾号表示 bit[i] = 1023 - bit[i]; } int ans = -1; // 枚举所有可能拥有的尾号表示 for (int i = 1; i <= 1023; ++i) { int cnt = 0; for (int j = 0; j < 10; ++j) { if (((i >> j) & 1) == 1) cnt += 1; } // 判断拥有的尾号与 7 天每天的不限行尾号是否有交集 // 如果都有交集就说明这个尾号表示是合法的 // 如果存在至少 1 天与这个尾号表示无交集,就说明这个尾号表示是不合法的 boolean ok = true; for (int j = 0; j < 7; ++j) { if ((bit[j] & i) == 0) { ok = false; break; } } // 如果合法,取最少的尾号数量 if (ok) { if (ans == -1) ans = cnt; else ans = Math.min(ans, cnt); } } System.out.println(ans); } }
Go
package main import ( "fmt" ) func main() { bit := make([]int, 7) c := make([]int, 7) for i := 0; i < 7; i++ { fmt.Scan(&c[i]) for j := 0; j < c[i]; j++ { var x int fmt.Scan(&x) // 先将第 i 天限行的每个尾号用二进制记录下来 bit[i] |= 1 << x } // 通过 1111 1111 11 这个二进制表示全部尾号,减去限行的尾号,就是第 i 天不限行的尾号表示 bit[i] = (1 << 10) - 1 - bit[i] } ans := -1 // 枚举所有可能拥有的尾号表示 for i := 1; i < 1<<10; i++ { cnt := 0 for j := 0; j < 10; j++ { if i>>j&1 == 1 { cnt++ } } // 判断拥有的尾号与 7 天每天的不限行尾号是否有交集 // 如果都有交集就说明这个尾号表示是合法的 // 如果存在至少 1 天与这个尾号表示无交集,就说明这个尾号表示是不合法的 ok := true for j := 0; j < 7; j++ { if i&bit[j] == 0 { ok = false break } } // 如果合法,取最少的尾号数量 if ok { if ans == -1 { ans = cnt } else { ans = min(ans, cnt) } } } fmt.Println(ans) } func min(a, b int) int { if a < b { return a } return b }
Js
process.stdin.resume(); process.stdin.setEncoding('utf-8'); let input = ''; process.stdin.on('data', (data) => { input += data; return; }); process.stdin.on('end', () => { const lines = input.trim().split('\n'); const bit = new Array(7); const c = new Array(7); let index = 0; for (let i = 0; i < 7; i++) { const line = lines[index++].split(' '); c[i] = line[0]; bit[i] = 0; for (let j = 0; j < c[i]; j++) { // 先将第 i 天限行的每个尾号用二进制记录下来 const x = line[j + 1]; bit[i] |= 1 << x; } // 通过 1111 1111 11 这个二进制表示全部尾号,减去限行的尾号,就是第 i 天不限行的尾号表示 bit[i] = (1 << 10) - 1 - bit[i]; } let ans = -1; // 枚举所有可能拥有的尾号表示 for (let i = 1; i < 1 << 10; ++i) { let cnt = 0; for (let j = 0; j < 10; ++j) { if ((i >> j) & 1) cnt += 1; } // 判断拥有的尾号与 7 天每天的不限行尾号是否有交集 // 如果都有交集就说明这个尾号表示是合法的 // 如果存在至少 1 天与这个尾号表示无交集,就说明这个尾号表示是不合法的 let ok = true; for (let j = 0; j < 7; ++j) { if ((i & bit[j]) == 0) { ok = false; break; } } // 如果合法,取最少的尾号数量 if (ok) { if (ans == -1) { ans = cnt; } else { ans = Math.min(ans, cnt); } } } console.log(ans); });