有一个 n × m n\times m n×m的网格图,其中一些格子上有一个炸弹,一些格子上有一个水晶,剩下的格子为空地。
炸弹可以引爆,每当一个炸弹被引爆,它必须选择:引爆它所在的这一行或者引爆它所在的这一列。水晶被引爆后会消失(即如果一个水晶所在的行列均被引爆,也只计算一次),炸弹被引爆后,会产生连锁反应,被引爆的炸弹也会选择引爆它所在的行或者列,如此下去直到没有新的炸弹被引爆。
现在你可以选择一个炸弹和其引爆的方向,请你求出最多可以引爆多少个水晶。
有 k k k个水晶, b b b个炸弹。
1 ≤ n , m ≤ 3000 , 0 ≤ k + b ≤ n m 1\leq n,m\leq 3000,0\leq k+b\leq nm 1≤n,m≤3000,0≤k+b≤nm
时间限制 3000 m s 3000ms 3000ms,空间限制 512 M B 512MB 512MB
一个炸弹引爆之后,它的方向是固定的,如果被竖着引爆,那么应该选择横着引爆,否则选择竖着引爆。
那么,我们把每一行和每一列都看作一个点,则总共有 n + m n+m n+m个点。对于每个炸弹,设炸弹的位置为 ( x , y ) (x,y) (x,y),那么就将第 x x x行对应的点和第 y y y列对应的点连边,表示第 x x x行被炸到时,通过这个炸弹可以炸到第 y y y列。
建好了图,引爆一个炸弹就相当于遍历这个炸弹所在的行或列对应的点所在的连通块,对于所有能遍历到的点,判断这些点对应的行和列总共能涉及到哪些水晶。
当然,我们发现这种做法有一些问题,因为被引爆的炸弹的行和列对应的点是被这个炸弹作为边连接的,所以我们引爆炸弹实际上是将这个点所在的行和列都引爆了。下面,我们考虑如何减去多计算的贡献。
对于第三种情况,如果引爆对应的行或列更优(也就是只得到这一行或列的水晶比连锁反应所能得到的水晶更多),那这部分的贡献怎么没有体现呢?
因为度为 1 1 1的点的数量大于等于 2 2 2,所以假如引爆度为 1 1 1的点 u u u对应的行或列会更优,则在不引爆另一个度为 1 1 1的点 v v vd对应的行或列时,点 u u u对应的行或列是会被引爆的,那它对答案的贡献就体现出来了。
所以,这样做是可行的。
那如何求一个原本会消失的水晶在去掉一行或一列之后是否还会消失呢?我们只需要记录每个水晶被引爆的次数,之后在去掉一行或一列时,遍历这一行或这一列上的每个水晶,如果水晶只被引爆了一次,则这个水晶就不会消失;否则,这个水晶还会消失。
时间复杂度为 O ( ( n + m ) 2 ) O((n+m)^2) O((n+m)2)。
#include
using namespace std;
const int N=3000;
int n,m,K,B,tot=0,d[2*N*N+5],l[2*N*N+5],r[2*N+5];
int cl=0,fl,now,mx,ans=0,wt[2*N+5],cm[2*N+5],z[N+5][N+5],hv[N+5][N+5];
char ch;
vector<int>v[2*N+5];
void add(int xx,int yy){
l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
void dfs1(int u,int fa){
cm[u]=1;
if(u<=n){
for(int i=0;i<v[u].size();i++){
if(z[u][v[u][i]]!=cl){
z[u][v[u][i]]=cl;
hv[u][v[u][i]]=0;
++now;
}
++hv[u][v[u][i]];
}
}
else{
for(int i=0;i<v[u].size();i++){
if(z[v[u][i]][u-n]!=cl){
z[v[u][i]][u-n]=cl;
hv[v[u][i]][u-n]=0;
++now;
}
++hv[v[u][i]][u-n];
}
}
for(int i=r[u];i;i=l[i]){
if(cm[d[i]]){
if(d[i]!=fa) fl=1;
continue;
}
dfs1(d[i],u);
}
}
void dfs2(int u){
cm[u]=2;
if(u<=n&&wt[u]==1){
int tmp=now;
for(int i=0;i<v[u].size();i++){
if(hv[u][v[u][i]]==1) --tmp;
}
mx=max(mx,tmp);
}
else if(u>n&&wt[u]==1){
int tmp=now;
for(int i=0;i<v[u].size();i++){
if(hv[v[u][i]][u-n]==1) --tmp;
}
mx=max(mx,tmp);
}
for(int i=r[u];i;i=l[i]){
if(cm[d[i]]==2) continue;
dfs2(d[i]);
}
}
int main()
{
// freopen("boom.in","r",stdin);
// freopen("boom.out","w",stdout);
scanf("%d%d%d%d",&n,&m,&K,&B);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
ch=getchar();
while(ch!='.'&&ch!='k'&&ch!='b') ch=getchar();
if(ch=='b'){
add(i,n+j);add(n+j,i);
++wt[i];++wt[n+j];
}
else if(ch=='k'){
v[i].push_back(j);
v[n+j].push_back(i);
}
}
}
for(int i=1;i<=n+m;i++){
if(wt[i]&&!cm[i]){
fl=0;now=0;++cl;
dfs1(i,0);
mx=0;dfs2(i);
if(fl) ans=max(ans,now);
else ans=max(ans,mx);
}
}
printf("%d",ans);
return 0;
}