首页 新闻 会员 周边

紫书uva1602,求解连通块种数

0
悬赏园豆:50 [待解决问题]

代码如下。题目大意是给定连通块大小n,输入平面大小h,w,要求在这样大小的平面内大小为n的连通块有多少种,其中经过平移,旋转,对称的同一连通块视为一种
  我的思路是在这个平面内从左上角开始搜索,对于每个没有放置的格子,分别搜索是否放置,如果搜到了答案,则用change函数将该答案所有可能的变化存起来,之后搜到答案后进行判重,dfs里的后四个参数分别记录的是此时搜到的连通块的边界情况
  现在的问题是我的程序得到的答案有些出入。。请问我的思路有问题吗(暂不考虑时间复杂度),如果没有,为什么结果错误?

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 10;
int n,w,h;
int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};
int ans;
bool vis[MAXN + 5][MAXN + 5],shape[40000][MAXN + 5][MAXN + 5];
int num;
bool check(int x,int y){
	for(int i = 0; i < 4; i++){
		int tx = x + dx[i];
		int ty = y + dy[i];
		if(x >= 1 && x <= w && y >= 1 && y <= h && tx >= 1 && ty <= h && tx <= w && ty >= 1 && vis[tx][ty])return 1;
	}
	return 0;
}
void out(){
	for(int i = 1; i <= w; i++){
		for(int j = 1; j <= h; j++){
			cout << vis[i][j]; 
		}
		puts("");
	}
	puts("");
}
bool is_(int le,int ri,int hi,int lo){
	bool flag,flag1 = 1;
		for(int i = 1; i <= num; i++){
			flag = 0;
			for(int j = 1; j <= lo; j++){
				for(int k = 1; k <= ri; k++){
					if(vis[j + hi - 1][k + le - 1] != shape[i][j][k]){
						flag = 1;
						break;
					}
				}
				if(flag)break;
			}
			if(!flag){
				flag1 = 0;
				return 0;
			}
		}
		return 1;
}
void change(int le,int ri,int hi,int lo){
	for(int i = hi; i <= lo; i++){
			for(int j = le; j <= ri; j++){
				shape[num + 1][i - hi + 1][j - le + 1] = vis[i][j];//原本形态 
				shape[num + 2][i - hi + 1][j - le + 1] = vis[i][ri - j + 1];//原本形态左右翻转翻转 
				}
			}
	num += 2;
	for(int i = hi; i <= lo; i++){
		for(int j = le; j <= ri; j++){
		shape[num + 1][lo - i + 1][j - le + 1] = vis[i][j];//上下翻转
		shape[num + 2][lo - i + 1][j - le + 1] = vis[i][ri - j + 1];//上下翻转后再左右翻转 
		}
	} 
	num += 2;
	if((ri - le + 1) <= n && (lo - hi + 1) <= n){
		for(int i = ri; i >= le; i--){
			for(int j = hi; j <= lo; j++){
				shape[num + 1][ri - i + 1][j - hi + 1] = vis[j][i];//逆时针旋转九十度 
				shape[num + 2][ri - i + 1][j - hi + 1] = vis[lo - j + 1][i];//逆时针旋转九十度后再左右对称 
			}
		}
		num += 2; 
		for(int i = le; i <= ri; i++){
			for(int j = hi; j <= lo; j++){
				shape[num + 1][i - le + 1][j - hi + 1] = vis[j][i];//逆时针旋转九十度后上下翻转 
				shape[num + 2][i - le + 1][j - hi + 1] = vis[lo - j + 1][i];//左右翻转 
			}
		}
		num += 2;
	}
}
void dfs(int x,int y,int cnt,int le,int ri,int hi,int lo){//坐标,连通块大小,最左,最右,最上,最下 
	if(cnt >= n){
		if(is_(le,ri,hi,lo) && cnt == n){
			out();
			ans++;
			change(le,ri,hi,lo);
		}
		return;
	}
	if(x > w || y > h)return;
	if((check(x,y) && cnt < n) || cnt == 0){
		vis[x][y] = 1;
		if(y > 1 && !vis[x][y - 1] && cnt + 2 <= n){//十字情况 
			vis[x][y - 1] = 1;
			if(y + 1 <= h)dfs(x,y + 1,cnt + 2,min(le,y - 1),max(ri,y),min(hi,x),max(lo,x));//是否到边缘 
			else dfs(x + 1,1,cnt + 2,min(le,y - 1),max(ri,y),min(hi,x),max(lo,x));
			vis[x][y - 1] = 0;//回溯 
		}
		if(y + 1 <= h)dfs(x,y + 1,cnt + 1,min(le,y),max(ri,y),min(hi,x),max(lo,x));
		else dfs(x + 1,1,cnt + 1,min(le,y),max(ri,y),min(hi,x),max(lo,x));
		vis[x][y] = 0;
	}
	vis[x][y] = 0;
	if(y + 1 <= h){
		dfs(x,y + 1,cnt,le,ri,hi,lo);
	}
	else dfs(x + 1,1,cnt,le,ri,hi,lo);
	return;
}
int main(){
	cin >> n >> h >> w;
	dfs(1,1,0,99,0,99,0);//左上角开始,对于每一个格子,判一下是否放一个灰块 
	change(1,4,1,5);
	cout << ans << endl;
}

</details>

腾云今天首飞了吗的主页 腾云今天首飞了吗 | 初学一级 | 园豆:152
提问于:2022-07-04 22:10
< >
分享
所有回答(1)
0

你的程序和思路看起来都是正确的。这个程序是寻找所有可能的连通块,并通过一系列变换(旋转、翻转等)去除重复的结果。你的dfs函数能够确保搜索到所有可能的连通块,而change函数能够把每一个连通块所有可能的变化存起来。同时,你的is_函数可以正确判断一个新的形状是否已经存在。

然而,我觉得可能出现错误的原因是在is_函数中,你只对当前形状进行了一次匹配,如果它在数据库中没有直接的匹配,你就将其视为新的形状。但是,我们知道一个形状可能经过旋转或翻转后与数据库中的形状相匹配。所以,我建议你在is_函数中增加对形状所有可能变换的检查。

另外,你在dfs函数中多次使用了固定值 99 作为参数,这可能会导致某些边界条件的错误。应该使用变量代替这些固定值,或者在注释中说明为什么使用这些特定的值。

最后,我建议你添加更多的注释来解释代码的工作原理,这对于理解你的代码非常有帮助。

npe0 | 园豆:1299 (小虾三级) | 2023-12-18 11:49
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册