【递推】【DFS】【枚举】Gym

网格里放了一些石块,一个炸弹能炸开其所在的行和列。问炸光石块至少要几个炸弹。

枚举不炸开的行数,则可以得出还要炸开几列。

为了不让复杂度爆炸,需要两个优化。

先是递推预处理出f(i)表示i的二进制位中1的个数,f(i)=f(i-2^k)+1,k是可以推算出来的。

还要DFS枚举不炸开的行数,防止重复访问。(类似容斥的时候的写法)

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,f[1<<25],g[1<<25],zt[26],ans=2147483647;
char a[30][30];
void dfs(int S,int cur,int now){
	ans=min(ans,n-now+(f[S]-(n-now)<0 ? 0 : f[S]-(n-now)));
	for(int i=cur+1;i<=n;++i){
		dfs(S|zt[i],i,now+1);
	}
}
int main(){
	freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%s",a[i]+1);
		for(int j=1;j<=m;++j){
			if(a[i][j]=='*'){
				zt[i]|=(1<<(j-1));
			}
		}
	}
	for(int i=1,j=0;i<(1<<m);++i){
		if(i==(1<<j)){
			++j;
		}
		f[i]=f[i-(1<<(j-1))]+1;
	}
	dfs(0,0,0);
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/autsky-jadek/p/7171947.html