破坏正方形
破坏正方形
首先计算出火柴总数和正方形总数。
考虑横着的火柴有 \(n+1\) 行,每行有 \(n\) 根,竖着的同理(旋转 \(90°\)),所以一共有 \(2n(n+1)\) 根火柴。
边长为 \(1\) 的正方形有 \(n^2\) 个,\(2,(n-1)^2;3,(n-2)^2;\dots;n,1^2\),\(\sum_{i=1}^n i^2=\dfrac{n(n+1)(2n+1)}{6}\) 。所以共有这么多正方形。
然后分析题目。这是一个重复覆盖问题(不存在多项式做法,经典做法:Dacing Links
)。可以抽象出一个二维矩阵,每一列代表所有的正方形,每一行代表每一根火柴,\((i,j)\) 表示第 \(i\) 根火柴是否是第 \(j\) 个正方形的边。这道题就用朴素做法。
首先搜索顺序很简单,就按照没有被破坏的正方形进行搜索,可以按照从小到大的边长,因为小的边长分支会少一些(之前许多搜索题目的共性),枚举边上的火柴即可。估价函数根据 Dacing Links
中的,是枚举每个正方形,如果是完整的,就删除所有边,并算作删除一次。显然这个估价是正确的。
还有就是如果快速求出某个正方形的边。左右相差 \(1\) 根,上下则跨过 \(n\) 横 \(n+1\) 竖,共 \(2n+1\) 根。
#include<bits/stdc++.h>
using namespace std;
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=r;i>l;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define wh(i) while(i--)
const int N=61;
int T,n,m;
vector<int>s[N];
bool st[N];
bool check(int x){
for(int v:s[x])
if(st[v])return 0;
return 1;
}
int f(){
static bool state[N];
memcpy(state,st,sizeof st);
int cnt=0;
L(i, m){
if(check(i)){
++cnt;
for(int v:s[i])st[v]=1;
}
}
memcpy(st,state,sizeof st);
return cnt;
}
bool dfs(int dep,int max_dep){
if(dep+f()>max_dep)return 0;
L(i, m){
if(check(i)){
for(int v:s[i]){
st[v]=1;
if(dfs(dep+1,max_dep))return 1;
st[v]=0;
}
return 0;
}
}
return 1;
}
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
scanf("%d",&T);
wh(T){
memset(st,0,sizeof st);
m=0;
scanf("%d",&n);
int d=2*n+1;
Ls(len, 1, n+1)
Ls(a, 1, n+2-len)
Ls(b, 1, n+2-len){
auto &sq=s[m++];
sq.clear();
L(i, len){
sq.push_back((a-1)*d+b+i);
sq.push_back((a-1)*d+b+i+len*d);
sq.push_back((a-1)*d+b+n+i*d);
sq.push_back((a-1)*d+b+n+i*d+len);
}
}
int k;
scanf("%d",&k);
wh(k){
int x;
scanf("%d",&x);
st[x]=1;
}
int dep=0;
while(!dfs(0, dep))++dep;
printf("%d\n",dep);
}
return 0;
}