E - Red Polyomino 关于回溯 和爆搜

这题就是爆搜。。虽然看似有2^(nn)的复杂度。。
但是实际上因为相连的限制。。种类非常有限。。样例8
8的就可以看出来。
所以就是爆搜而已。。

记录这题是因为。之前一直在思考回溯 到底和爆搜什么关系。。
目前算是阶段性的一个理解。。
回溯只不过是爆搜的一种方式而已。。
如果我们可以每层递归 都是拷贝。而不是引用。。实际上是不需要回溯的。

回溯只在于样本只有一份。就是传引用的时候。我们只有通过恢复现场。。来尝试其他的可能。
下面两个版本的写法。。就证明了这点。。
总结:回溯只不过是恢复现场的一种手法。。如果现场不需要恢复(每层都拷贝一份新的) 就不需要回溯
而爆搜是一种枚举所有可能的手法。。分步X分类。比较经典的爆搜模型 就是完全二叉树 每个节点两种可能 选和不选。
题目链接

#include 
using namespace std;
#define int long long
#define ll __int128_t
#define ar array<int, 2>
#define arr array<int, 3>
int  n, m, k, inf = 1LL << 61, mod = 998244353;// 1e9+7;
const int N = 5e5 + 50;
int ans;
void dfs(vector<string>s) {
	int cnt = 0;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < n; ++j) {
			cnt += s[i][j] == 'o';
		}
	if (cnt == k) {
		ans++;
		return ;
	}
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < n; ++j) {
			if (s[i][j] != '.')
				continue;
			if (cnt) {
				int d[5] {1, 0, -1, 0, 1};
				bool ok = 0;
				for (int c = 0; c < 4; ++c) {
					int x = i + d[c], y = j + d[c + 1];
					if (x >= 0 && y >= 0 && x < n && y < n && s[x][y] == 'o')
						ok = 1;
				}
				if (ok == 0)//就是旁边一定要有红色的。。因为相连
					continue;
			}
			s[i][j] = 'o';
			dfs(s);
			s[i][j] = '#';
			dfs(s);
			//我们传的是整个拷贝的s。这边就不需要回溯。
			return ;
		}
}
void solve() {
	cin >> n >> k;
	vector<string>a(n);
	for (auto&a : a)
		cin >> a;

	dfs(a);

	cout << ans;
};





signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(15);
#ifdef DEBUG
	freopen("../1.in", "r", stdin);
#endif
	//init_f();
	//init();
	//expr();
	// int T; cin >> T; while(T--)
	solve();
	return 0;
}



#include 
using namespace std;
#define int long long
#define ll __int128_t
#define ar array<int, 2>
#define arr array<int, 3>
int  n, m, k, inf = 1LL << 61, mod = 998244353;// 1e9+7;
const int N = 5e5 + 50;
int ans;
void dfs(vector<string>&s) {
	int cnt = 0;
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < n; ++j) {
			cnt += s[i][j] == 'o';
		}
	if (cnt == k) {
		ans++;
		return ;
	}
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < n; ++j) {
			if (s[i][j] != '.')
				continue;
			if (cnt) {
				int d[5] {1, 0, -1, 0, 1};
				bool ok = 0;
				for (int c = 0; c < 4; ++c) {
					int x = i + d[c], y = j + d[c + 1];
					if (x >= 0 && y >= 0 && x < n && y < n && s[x][y] == 'o')
						ok = 1;
				}
				if (ok == 0)//就是旁边一定要有红色的。。因为相连
					continue;
			}
			s[i][j] = 'o';
			dfs(s);
			s[i][j] = '#';
			dfs(s);
			s[i][j] = '.';//回溯本质上就多了这一步而已。。但是可以传引用。。!!!!
			return ;
		}
}
void solve() {
	cin >> n >> k;
	vector<string>a(n);
	for (auto&a : a)
		cin >> a;

	dfs(a);

	cout << ans;
};


// 回溯只不过是为了还原现场而已。。如果传的不是引用。。。每个图都拷贝一份。那么根本不需要回溯!!!
//回溯只不过是爆搜的一种实现策略。。而已。。每个图都拷贝一份。那么根本不需要回溯!
// 






signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout << fixed << setprecision(15);
#ifdef DEBUG
	freopen("../1.in", "r", stdin);
#endif
	//init_f();
	//init();
	//expr();
	// int T; cin >> T; while(T--)
	solve();
	return 0;
}



你可能感兴趣的:(算法)