公式求解:容斥原理
时间复杂度 $O(1)$
思路
题意是给定两个3位密码(类比两把锁)且相差2以内的数可选。
思路是锁1可选数+锁2可选数-锁1锁2公共可选数。
注意点:
计算可选数时一定是要考虑拨盘总数n !!!
1、计算锁1/锁2分别可选数:
-
情况一:当不受限于拨盘总数n,即n > 5:
锁1:A-2~A+2;B-2~B+2;C-2~C+2
锁2:D-2~D+2;E-2~E+2;F-2~F+2
所以锁1可选数:5 * 5 * 5,锁2可选数:5 * 5 * 5 -
情况二:当受限拨盘总数n,即n <= 5,给定的3位密码也一定在n以内,此时可选数为1~n
所以锁1可选数:n * n * n,锁2可选数:n * n * n
综上,single = 锁1/锁2可选数:Math.min(n,5) * Math.min(n,5) * Math.min(n,5)
2、计算锁1锁2公共可选数
-
先计算锁1和锁2密码相同位上数的距离d:
一般情况下两数之间距离为Math.abs(a - b)
但是在圆环中当a和b的距离太远,则需要取如图a和b反向的那段较小的距离,即n - Math.abs(a - b)
因此,d = Math.min(Math.abs(a[i] - b[i]),n - Math.abs(a[i] - b[i]))
-
情况一:当不受限于拨盘总数n,即n > 5:
枚举可得规律:
当d > 5,公共可选数0;当d <= 5,公共可选数5 - d
所以每一位上的公共可选数为:Math.max(0,5-d) -
情况二:当受限拨盘总数n,即n <= 5:
所以每一位上的公共可选数为n
综上:锁1锁2公共可选数为:
both = Math.min(Math.max(0,5-d1),n) * Math.min(Math.max(0,5-d2),n) * Math.min(Math.max(0,5-d3),n)
最终答案为single * 2 - both
Java 代码
import java.util.*;
public class Main{
static int[] a = new int[3];
static int[] b = new int[3];
static int n;
public static int both(){
int res = 1;
for(int i = 0;i < 3;i++){
int d = Math.min(Math.abs(a[i] - b[i]),n - Math.abs(a[i] - b[i]));
int r = Math.min(n,Math.max(0,5 - d));
res *= r;
}
return res;
}
public static int single(){
int res = 1;
for(int i = 0;i < 3;i++){
res *= Math.min(n,5);
}
return res;
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i = 0;i < 3;i++){
a[i] = sc.nextInt();
}
for(int i = 0;i < 3;i++){
b[i] = sc.nextInt();
}
int res = single() * 2 - both();
System.out.println(res);
}
}
暴力枚举
时间复杂度$O(n^3)$
注意点:
所求答案的三位必须与锁1上的三位数对应满足 或 与锁2上的三位数对应满足,
而不是答案的某几位和锁1对应的那几位满足,而其他位和锁2的对应位满足!
Java 代码
import java.util.*;
public class Main{
static int[] a = new int[3];
static int[] b = new int[3];
static int n;
static int cnt;
//检查i 与 c数组的第u位数是否距离在2以内
public static boolean check(int[] c,int i,int u){
int d = Math.min(Math.abs(i - c[u]),n - Math.abs(i - c[u]));
return d <= 2;
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i = 0;i < 3;i++){
a[i] = sc.nextInt();
}
for(int i = 0;i < 3;i++){
b[i] = sc.nextInt();
}
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++){
for(int k = 1;k <= n;k++){
if(check(a,i,0) && check(a,j,1) && check(a,k,2) ||
check(b,i,0) && check(b,j,1) && check(b,k,2)){
cnt++;
}
}
}
}
System.out.println(cnt);
}
}
当n > 5时, d <= 5 时, 两边都有可能出现公共部分,都需要判断
容斥原理题解,求公共部分的时候少考虑了情况,(枚举可得规律)还是不大靠谱的。可以直接用数组打标记求公共部分,这样会稳妥很多。错误数据可能是后来加的:
n=6
密码1:111
密码2:444
结果:186
。经提示,C++代码现修改如下:
用
st
状态数组标记,求解每一位交集元素的个数即可好像都有些问题。。
感谢楼主分享,补充一下容斥原理c++的代码: