建信04. 电学实验课

建信04. 电学实验课

本题传送门

  • 题意:
    某电学实验使用了 row * col 个插孔的面包板,可视作二维矩阵,左上角记作 (0,0)。老师设置了若干「目标插孔」,它们位置对应的矩阵下标记于二维数组 position。实验目标要求同学们用导线连接所有「目标插孔」,即从任意一个「目标插孔」沿导线可以到达其他任意「目标插孔」。受实验导线长度所限,导线的连接规则如下:
  • 1、一条导线可连接相邻两列的且行间距不超过 1 的两个插孔
  • 2、每一列插孔中最多使用其中一个插孔(包括「目标插孔」)
    若实验目标可达成,请返回使用导线数量最少的连接所有目标插孔的方案数;否则请返回 0。

注意
输入数据保证每列最多仅有一个「目标插孔」;
答案需要以 1e9 + 7 (1000000007) 为底取模,如:计算初始结果为:1000000008,请返回 1;

  • 样例
    输入:row = 5, col = 6, position = [[1,3],[3,2],[4,1]]
输出:0
    Alt
    Alt
    提示:
  • 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;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值