建信04. 电学实验课
- 题意:
某电学实验使用了 row * col 个插孔的面包板,可视作二维矩阵,左上角记作 (0,0)。老师设置了若干「目标插孔」,它们位置对应的矩阵下标记于二维数组 position。实验目标要求同学们用导线连接所有「目标插孔」,即从任意一个「目标插孔」沿导线可以到达其他任意「目标插孔」。受实验导线长度所限,导线的连接规则如下:
- 1、一条导线可连接相邻两列的且行间距不超过 1 的两个插孔
- 2、每一列插孔中最多使用其中一个插孔(包括「目标插孔」)
若实验目标可达成,请返回使用导线数量最少的连接所有目标插孔的方案数;否则请返回 0。
注意
输入数据保证每列最多仅有一个「目标插孔」;
答案需要以 1e9 + 7 (1000000007) 为底取模,如:计算初始结果为:1000000008,请返回 1;
- 样例
提示: - 1 <= row <= 20
- 3 <= col <= 10^9
- 1 < position.length <= 1000
- 0 <= position[i][0] < row
- 0 <= position[i][1] < col
题解:看到这道题时,我以为是动态规划,结果一看 3 <= col <= 10^9 瞬间蒙了。直接去找答案了。看到这个数据范围一看就知道是有关位运算的。解答这道题首先需要点矩阵乘法的知识。
如果矩阵能够相乘,必然存在如果矩阵A 的大小为 a x b 那么 矩阵B 的大小 b x c,相乘得到矩阵大小为 a x c;
vector<vector<int>> matrix(vector<vector<int>>& a, vector<vector<int>>& b){// 看图理解代码
int row1 = a.size(), col1 = a[0].size(), row2 = b.size(), col2 = b[0].size();
if(col2==row1) {
swap(a, b);//如果axb 是 x*y x z*x交换一下a, b矩阵便于计算
swap(row1, row2), swap(col1, col2);
}
vector<vector<int>> c(row1, vector<int>(col2, 0));
for(int i = 0; i < row1; i++){
for(int k = 0; k < col1; k++){
if(!a[i][k]) continue;//贡献为0;
for(int j = 0; j < col2; j++){
c[i][j] = c[i][j] + a[i][k]*b[k][j];
}
}
}
return c;
}
- 我们用一个矩阵a[ i ][ j ]表示第 i 行是否可以 转移到第 j 行,同时我们可以预处理出来这个矩阵;
- 下面我们要思考从第 i 行到第 j 行 的方案数;
第一个矩阵的 i 和 j 代表第 i 行和第 j 列是否可达,第二个矩阵代表初始状态;如果两个矩阵相乘会发生什么呢?是不是就得到了向右移动一列的第 i 行 到第 j 列的方案数,如果要是在向右移动一列呢?就是再乘上第一个矩阵。每乘一次第一个矩阵就相当于向右移动一列。(请仔细思考)
- 有了这个思路结合 row 的范围,设 y = position[ i ] - position[ i - 1 ], 计算每两个点之间的方案数,再相乘就是答案。
- 对于每一个 y, 我们可以枚举它的每一位二进制位,如果该位为1,就意味着要乘上 ( 1 << j ) ( j 代表二进制位) 个可达矩阵。所以需要预处理出来这部分。
#define ll long long
const int N = 20, mod = 1e9 + 7;
struct M{
int a[30][30], len;
M(){
memset(a, 0, sizeof a);
}
};
M operator * (const M & a, const M & b){
M c;
for(int i = 0; i < a.len; i++){
for(int k = 0; k < a.len; k++){
if(a.a[i][k]==0) continue;
for(int j = 0; j < a.len; j++){
c.a[i][j] = (c.a[i][j] + (ll)(a.a[i][k]) * b.a[k][j]) % mod;
}
}
}
c.len = a.len;
return c;
}
class Solution {
public:
int electricityExperiment(int row, int col, vector<vector<int>>& position) {
M trans[30];
for(int i = 0; i < row; i++){
for(int j = max(0, i-1); j <= min(row-1, i+1); j++){
trans[0].a[i][j] = 1;
}
}
trans[0].len = row;
M p;
for(int i = 0; i < row; i++){
p.a[i][i] = 1;
}
p.len = row;
for(int i = 1; i < 30; i++){
trans[i] = trans[i-1]*trans[i-1];
}
sort(position.begin(), position.end(), [&](const vector<int> x, const vector<int> y){
return x[1]<y[1];
});
int ans = 1;
for(int i = 1; i < position.size(); i++){
int y = position[i][1] - position[i-1][1];
M res = p;
for(int j = 0; j < 30; j++){
if(y&(1<<j)){
res = res*trans[j];
}
}
ans = (ll)ans *(res.a[position[i-1][0]][position[i][0]])%mod;
if(!ans) return 0;
}
return ans;
}
};