uva 1603:
这题比较难得是怎么存储所有的正方形;
我的方法:
以边的长度来决定正方形的大小; 首先遍历所有的边长:
//len :正方形的边长 n: 题目所给的最大边长;
for(int len = 1; len <= n; i++){
}
当n = k;会组成 由K * K的小正方形组成的大正方形;
这是个 n = 3的大正方形;
可以从小正方形出现遍历每一个边长为“len”的正方形;
//遍历每一个小正方形
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
}
}
//遍历每一个边长为“len”的正方形
for(int len = 1; len <= n; i++){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
}
}
}
解决了遍历问题,还需要解决如何确定每一个边的位置;
在题目中,已经给每个火柴棍编号了(1 到 2n(n + 1) );
第 i 行第 j 列的边长为 1 的正方形的各边:
-
上边: (i - 1) * (2 * n + 1) + j + len * (2 * n + 1);//len = 0时, 就是上边
-
下边:(i - 1) * (2 * n + 1) + j + len * (2 * n + 1);
-
左边:(i - 1) * (2 * n + 1) + j + len + n; //len = 0时, 就是左边;
-
右边:(i - 1) * (2 * n + 1) + j + len + n;
读者不妨将值代入验算一下
//将上边与下边合成一个函数
int get_ud(int i, int j, int len) {
return (i - 1) * (2 * n + 1) + j + len * (2 * n + 1);
}
//将左边与右边合成一个函数
int get_lr(int i, int j, int len) {
return (i - 1) * (2 * n + 1) + j + len + n;
}
for(int len = 1; len <= 1; i++){
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
int up = get_ud(i, j, 0);
int down = get_ud(i, j, len);
int left = get_lr(i, j, 0);
int right = get_lr(i, j, len);
这里将正方形的各边保存下来;
//
}
}
}
//结束后求出了所有边长为 1 的正方形;
当边长大于 1 时,比较麻烦:
for (int len = 1; len <= n; len++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i + len - 1 > n || j + len - 1 > n)//边长不够组成正方形了,退出
break;
for (int k = 0; k < len; k++) {//生成所有的边。 分len次生成,每次生成4条边, 总共 4 * len条边;
int up = get_ud(i, j + k, 0);
int down = get_ud(i, j + k, len);
int left = get_lr(i + k, j, 0);
int right = get_lr(i + k, j, len);
}
nums++;
}
}
}
以上面那个图为例, 生成 2 * 2 的正方形:
第一次生成:1,4,6,15
第二次生成:2,11,16,13
之后就比较简单了, 保存二份所有的正方形的周长,一份不动,一份用于dfs()的遍历; 然后dfs()深度搜索,每次拿掉一个完整正方形的一根火柴(是否完整取决于该正方形的当前周长是否与他本来的周长相等),将缺少火柴的正方形周长减一;直到没有完整的正方形,算法结束;
坑:
memset()是以字节为单位进行初始化, 不可以初始化1;
IDA*算法,当数据量小时,可以不使用估价函数;只会减少一部分时间;
if (!get_legal_()) {
//cout << d << endl;
//maxd = d;
return true;
}
if (d >= maxd) return false;
这种情况,“d >= maxd”;用">=";
if (d >= maxd) return false;
if (!get_legal_()) {
//cout << d << endl;
//maxd = d;
return true;
}
这种情况,“d > maxd”;用">";
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<sstream>
using namespace std;
int maxn = 5, n, m, nums, maxd;
int arr[51][61], cur_size[61], full_size[61];
int get_sticks() {
return 2 * n * (n + 1);
}
int get_ud(int i, int j, int len) {
return (i - 1) * (2 * n + 1) + j + len * (2 * n + 1);
}
int get_lr(int i, int j, int len) {
return (i - 1) * (2 * n + 1) + j + len + n;
}
int get_legal_() {
for (int i = 1; i < nums; i++) {
//cout << cur_size[i] << " " << full_size[i] << endl;
if (cur_size[i] == full_size[i])
return i;
}
return 0;
}
int out;
//bool dfs(int d) {
/* 自己思考的
void dfs(int d) {
if (d >= maxd) return;
int pos = get_legal_();
//if (!pos) return true; //成功条件
if (!pos) {
out = d;
return;
}
//if (d >= maxd) return false; //到达深度
//if ((maxd - d) * n - nums > 0) return false; //IDA*启发函数
for (int i = 1; i <= get_sticks(); i++) {//n种接下来的走法
if (arr[pos][i]) {
for (int j = 1; j < nums; j++)
if (arr[j][i])
cur_size[j]--;
//if(dfs(d + 1)) return true;
dfs(d + 1);
for (int j = 1; j < nums; j++)
if (arr[j][i])
cur_size[j]++;
}
}
return ;
}
*/
bool dfs(int d) {
if (!get_legal_()) {
//cout << d << endl;
//maxd = d;
return true;
}
if (d >= maxd) return false;
int k = get_legal_(); //选择一个正方形拿掉其中边上一根火柴
for (int i = 1; i <= get_sticks(); i++) {
if (arr[k][i]) {
for (int j = 1; j < nums; j++)
if (arr[j][i]) cur_size[j]--;
if (dfs(d + 1)) return true;
for (int j = 1; j < nums; j++)
if (arr[j][i]) cur_size[j]++;
}
}
return false;
}
void solve() {
for (maxd = 1;; maxd++) {
if (dfs(0)) {
cout << maxd << endl;
break;
}
}
//maxd = n * n;
//cout << maxd;
//dfs(0);
//cout << maxd << endl;
return;
}
int main() {
int t;
int remov[61];
cin >> t;
while (t--) {
cin >> n >> m;
for (int i = 1; i <= get_sticks(); i++) remov[i] = 1;
//cout << remov[24] << endl;
//memset(arr, 0, sizeof(arr));
for (int i = 0; i < m; i++) {
int pos;
cin >> pos;
remov[pos] = 0;
}
nums = 1;
memset(arr, 0, sizeof(arr));
for (int len = 1; len <= n; len++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cur_size[nums] = 0;
full_size[nums] = 4 * len;
if (i + len - 1 > n || j + len - 1 > n)
break;
for (int k = 0; k < len; k++) {
int up = get_ud(i, j + k, 0);
int down = get_ud(i, j + k, len);
int left = get_lr(i + k, j, 0);
int right = get_lr(i + k, j, len);
//cout << up << " " << down << " " << left << " " << right << " ";
arr[nums][up] = 1;
arr[nums][down] = 1;
arr[nums][left] = 1;
arr[nums][right] = 1;
//cout << remov[up] << " " << remov[down] << " " << remov[left] << " " << remov[right] << endl;
cur_size[nums] += (remov[up] + remov[down] + remov[left] + remov[right]);
}
//cout << endl;
nums++;
}
}
}
solve();
}
}