题解:B3791 [信息与未来 2023] 电路布线

解 1:爆搜+剪枝

暴力搜索很明显是枚举每一个为 .的点保留原来的状态或者变为 +,最后进行判断。时间复杂度 2 n × m × n × m 2^{n\times m}\times n\times m 2n×m×n×m,第一个测试数据就是为了能够通过这样的数据构造的。

考虑剪枝优化。如果当前的答案之后就算加上了理论的全部可以为 +的地方还是比不过原来的答案,那么就可以直接返回。

这个很像 IDA-star 中的估价函数,只不过这里的最大深度是搜过的答案。

IDA-star 是用于应对最小答案,方法是将答案估小。而这里要求的是最答案,所以应当将答案估大。比如当前答案为 3 3 3,最优答案为 5 5 5。假如当前答案扩展出来的答案更优,但是估价函数将其估小估到了 4 4 4,那就会被击毙从而导致答案可能错误。

这里可以估成之后所有点都为 +的情况,一定不会 WA。

判断是否形成环也很好弄,并查集、dfs 或者 bfs 都是可以的。这里用了 dfs(带权并查集之前写挂了,现在还在痛恨并查集。),比如走着走着走到了之前标记的点,并且不是上一个点,那就有环。

发现这里的估价函数剪枝可能优化不了时间(原理同 K 短路的 A-star 算法被卡),还需要加上一些剪枝。

比如当前节点加上去就已经是有环的了,那么就不用往后搜。这个限制了指数(虽然本人很蒻算不出最少能卡多少个不对的情况),而且 dfs 判环最多 6 × 6 = 36 6\times 6=36 6×6=36,可以当成常数,所以在大数据下不比没剪枝的代码更劣。

加上去就能够 AC 了。

#include<bits/stdc++.h>
#define MAXN 17
using namespace std;
int n,m,sx,sy,cnt,ans;
char mp[MAXN][MAXN];
char ouf[MAXN][MAXN];
char dfn[MAXN][MAXN];
bool vis[MAXN][MAXN];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
inline int goal(const int &x,const int &y){
	return n*m-(x-1)*m-y;
}
bool check(const int &x,const int &y,const int &px,const int &py){
	if(vis[x][y]){
		return false;
	}
	vis[x][y]=true;
	--cnt;
	for(register int i=0;i<4;++i){
		const int nx=x+dx[i];
		const int ny=y+dy[i];
		if(px==nx&&py==ny){
			continue;
		}
		if(1<=nx&&nx<=n&&1<=ny&&ny<=m&&mp[nx][ny]=='+'){
			if(!check(nx,ny,x,y)){
				return false;
			}
		}
	}
	return true;
}
void dfs(int x,int y,const int &sum){
	if(y==m+1){
		++x;
		y=1;
	}
	if(sum+goal(x,y)<=ans){
		return;
	}
	if(x==n+1){
		memset(vis,0,sizeof(vis));
		cnt=sum;
		if(check(sx,sy,-1,-1)&&!cnt){
			memcpy(ouf,mp,sizeof(mp));
			ans=sum;
		}
		return;
	}
	if(mp[x][y]=='.'){
		mp[x][y]='+';
		memset(vis,0,sizeof(vis));
		if(check(x,y,-1,-1)){
			dfs(x,y+1,sum+1);
		}
		mp[x][y]='.';
	}
	dfs(x,y+1,sum);
}
int main(){
	register int sum=0;
	scanf("%d %d",&n,&m);
	for(register int i=1;i<=n;++i){
		scanf("%s",mp[i]+1);
		for(register int j=1;j<=m;++j){
			if(mp[i][j]=='+'){
				++sum;
				sx=i;
				sy=j;
			}
			ouf[i][j]=mp[i][j];
		}
	}
	dfs(1,1,sum);
	for(register int i=1;i<=n;++i){
		for(register int j=1;j<=m;++j){
			putchar(ouf[i][j]);
		}
		putchar('\n');
	}
	return 0;
}

解 2:折半搜索

还是原来的复杂度: 2 n × m × n × m 2^{n\times m}\times n\times m 2n×m×n×m,折半搜索还是指数级别的优化。

原理是把上半部分搜一遍,把下半部分搜一遍,时间复杂度 2 n × m 2 × 2 × n × m 2^{\frac{n\times m}{2}}\times 2\times n\times m 22n×m×2×n×m

但是具体怎么用折半搜索实现呢?Luogu 上目前没有,我从官方题解上找出了一个方法(本蒻蒻抛弃的自认为难写方法)。

枚举中转点,即在 n 2 \frac{n}{2} 2n 的位置,时间复杂度 2 m 2^m 2m。之后向两边扩散最大的位置,时间复杂度 2 × 2 n 2 × m 2\times 2^{\frac{n}{2}\times m} 2×22n×m,最后判断整个有没有环。

较为难写,本人还没有调出来,之后选择性地补上,希望大家不介意。求管理员大大的给过!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值