引爆炸弹
在一个 n×m 的方格地图上,某些方格上放置着炸弹。手动引爆一个炸弹以后,炸弹会把炸弹所在的行和列上的所有炸弹引爆,被引爆的炸弹又能引爆其他炸弹,这样连锁下去。
现在为了引爆地图上的所有炸弹,需要手动引爆其中一些炸弹,为了把危险程度降到最低,请算出最少手动引爆多少个炸弹可以把地图上的所有炸弹引爆。
数据范围:1≤n,m≤1000;
这问题其实与连通块类似。
我们把可以互相引爆的炸弹放到同一个集合中,那么最后划分的集合个数就是我们要求的答案。显然,引爆集合中任意一个炸弹造成的效果都一样,我们可以遍历nm的方格中所有炸弹,如果当前炸弹之前没被引爆,就引爆它,即标记他所在集合中的所有炸弹。
可以用DFS搜出一个炸弹所在的集合,每次引爆一个炸弹后,就搜它所在的行列是否有其他炸弹,然后继续搜索。因为每次炸弹最多有nm个,每个炸弹引爆后要检查所在行和列都有没有炸弹,时间复杂度是O(nm(n+m))
这里有一个剪枝,每行每列最多搜一遍,于是可以标记一下搜过的行列避免重复搜索,这样每个格子都最多被检查两边,时间复杂度为O(nm)
row和col分别表示每行、列是否考虑过引爆。
boom函数,首先标记当前炸弹已经被引爆。如果当前行没有被引爆,就应该循环判断该行是否有没有引爆的炸弹并引爆。
如果当前列没有引爆过,那就引爆当前列并标记,然后循环判断该列是否有没有引爆的炸弹,有就继续搜索。
在main函数里,我们要统计答案,只要遍历n*m的方格,每遇到一个没引爆的炸弹,答案就增加1,并调用boom函数引爆所有相关联的炸弹。
示例代码:
#include <stdio.h>
#include <iostream>
using namespace std;
char mat[1010][1010];
int n, m;
bool row[1010],col[1010];
void boom(int x,int y){
mat[x][y]=0;
if(!row[x]){
row[x]=true;
for(int i=0;i<m;i++){
if(mat[x][i]=='1'){
boom(x,i);
}
}
}
if(!col[y]){
col[y]=true;
for(int i=0;i<n;i++){
if(mat[i][y]=='1'){
boom(i,y);
}
}
}
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; ++i) {
scanf("%s", mat[i]);
}
int cnt=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(mat[i][j]=='1'){
cnt++;
boom(i,j);
}
}
}
cout<<cnt<<endl;
return 0;
}
生日蛋糕
今天是花椰妹的生日,蒜头君打算制作一个体积为 nπ 的 m 层生日蛋糕送给她。蛋糕每层都是一个圆柱体,设从下往上数第 i(0≤i<m)层蛋糕是半径为 R[i] , 高度为H[i]的圆柱。当 i>0 时,要求R[i]<R[i-1]且H[i]<H[i-1]
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积 Q 最小。令iQ=Sπ,请编程对给出的 n 和 m,找出蛋糕的制作方案(适当的 R[i]和H[i] 的值),使 S 最小。(除 Q 外,以上所有数据皆为正整数)
为了方便叙述,本体表面积和体积是除以pai以后的结果,且表面积是指除去下底面后的表面积。
整个蛋糕的体积 n = ∑ i = 0 m − 1 R i 2 H i n=\sum_{i=0}^{m-1}R_i^2H_i n=i=0∑m−1Ri2Hi
表面积 s = R 0 2 + 2 ∑ i = 0 m − 1 R i H i s=R_0^2+2\sum_{i=0}^{m-1}R_iH_i s=R