Square Destroyer POJ - 1084
题意:
给一个由(n+1)n2根火柴棒构成的边长为n正方形,每根火柴棒都有编号,已经删去了k根火柴棒,问至少再删多少根火柴棒可以破坏掉所有正方形。
思路:
开两个数组stick[][], square[][] (我用的是vector), stick[i]表示的是需要编号为i的木棒的正方形有哪些,square[i]表示的是编号为i的正方形需要的木棒有哪些。
exi[i]表示的是编号i的正方形是否存在:exi[i] == 0, 存在;每删去该正方形上的一根火柴,exi[i] - - 。这么做就解决了递归、回溯的问题。
观察一下木棒的编号规律,提前预处理出来正方形们以及木棒们(square,stick两个数组),开始搜索,找到一个还存在的正方形,枚举删除它的哪一根木棒,递归。为了节省时间,要找还存在的边长最小的正方形,这个在我们预处理square数组时,加入正方形的顺序就可以搞定。
估价函数:对于当前一个状态,枚举每个正方形是否存在,如果存在,就删除它的所有木棍,同时res++,然后继续枚举,最后返回res。
// square,stick数组处理技巧
#include<bits/stdc++.h>
using namespace std;
int main(){
//
int n;
scanf("%d", &n);
int totstick = (n+1)*n*2;
int totsquare = 0;
int neibor = n*2+1;
for(int sz = 1; sz <= n; sz++)
for(int i = 1; (i-1)/neibor+sz <= n; i += neibor)
for(int j = i; j-i+sz <= n; j++)
{
totsquare ++;
int l = j;
cout<< " sz = " << sz << " i = " << i << " j = " << j << " j-i+sz = " << j-i+sz << " l = " << l << " l-j = " << l - j;
for( l ; l-j < sz; l++)
{
// square[totsquare].push_back(l);
// square[totsquare].push_back(l+sz*neibor);
// stick[l].push_back(totsquare);
// stick[l+sz*neibor].push_back(totsquare);
cout<< " totsquare1 = " << totsquare << " : " << l <<' '<< l+sz*neibor<<' ';
}
for(int l = j+n; (l-j-sz)/neibor < sz; l += neibor)
{
// square[totsquare].push_back(l);
// square[totsquare].push_back(l+sz);
// stick[l].push_back(totsquare);
// stick[l+sz].push_back(totsquare);
cout<< " totsquare2 = "<< l <<' '<< l+sz<<' ';
}
cout<<endl<<endl;
}
}
IDA* code
#include<bits/stdc++.h>
using namespace std;
int T, n, k, totstick, totsquare, neibor, ans, maxd, exi[65], temp[65];
vector <int> stick[65]; // stick[i]记录每条边包含的正方形
vector <int> square[65]; // square[i]记录每个正方形包含的边
int h()//gu'jiguji
{
int res = 0;
for(int i = 1; i <= totsquare; i++) temp[i] = exi[i];
for(int i = 1; i <= totsquare; i++) if(!temp[i])
{
res ++;
temp[i] -= square[i].size();
for(int j = 0; j < square[i].size(); j++)
for(int l = 0; l < stick[square[i][j]].size(); l++)
{
temp[stick[square[i][j]][l]] --;
}
}
return res;
}
bool dfs(int sum)
{
if(sum+h() >= maxd) return 0;
int tmp = 1;
while(exi[tmp] < 0 && tmp <= totsquare) tmp++; // choose a square (tmp)
if(tmp > totsquare) {ans = min(sum, ans); return 1;}
for(int i = 0; i < square[tmp].size(); i++) // choose which in tmp to destroy
{
int sti = square[tmp][i];
for(int j = 0; j < stick[sti].size(); j++) exi[stick[sti][j]]--;
if(dfs(sum+1)) return 1;
for(int j = 0; j < stick[sti].size(); j++) exi[stick[sti][j]]++;
}
return 0;
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d %d", &n, &k);
totstick = (n+1)*n*2; totsquare = 0; neibor = n*2+1;
for(int i = 1; i < 65; i++)
{
stick[i].clear(); square[i].clear();
}
for(int sz = 1; sz <= n; sz++) //枚举正方形的边长
for(int i = 1; (i-1)/neibor+sz <= n; i += neibor) //枚举行
for(int j = i; j-i+sz <= n; j++) //枚举列
{
totsquare ++;
for(int l = j; l-j < sz; l++) //枚举正方形的上边
{
square[totsquare].push_back(l);
square[totsquare].push_back(l+sz*neibor); //上边对应的下边
stick[l].push_back(totsquare);
stick[l+sz*neibor].push_back(totsquare);
}
for(int l = j+n; (l-j-sz)/neibor < sz; l += neibor)
{
square[totsquare].push_back(l);
square[totsquare].push_back(l+sz);
stick[l].push_back(totsquare);
stick[l+sz].push_back(totsquare);
}
}
memset(exi, 0, sizeof exi);
for(int i = 1; i <= k; i++)
{
int t; scanf("%d", &t);
for(int j = 0; j < stick[t].size(); j++)
{
exi[stick[t][j]]--;
}
totstick --;
}
ans = totstick;
for(maxd = 1; ; maxd++) if(dfs(0)) break;
printf("%d\n", ans);
}
return 0;
}
DLX code:
// 先意思一下,懒得跑了,留着下次再找bug
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
vector <int> stick[65]; // squares that need stick[i]
vector <int> square[65]; // sticks that square[i] need
int T, n, k, totstick, totsquare, neibor, ans, exi[65], temp[65];
struct DLX{
int U[maxn], D[maxn], L[maxn], R[maxn], row[maxn], col[maxn], sum[maxn], h[maxn];
int n, m, time;
void init(int _n, int _m){
n = _n;
m = _m;
for(int i = 0; i <= m, i++) {
sum[i] = 0;
L[i] = i - 1;
R[i] = i + 1;
U[i] = D[i] = i;
row[i] = 0;
col[i] = i;
}
for(int i = 1; i <= n; i++)
h[i] = -1;
L[0] = m;
R[m] = 0;
time = m;
}
void link(int r, int c){
row[++time] = r;
col[time] = c;
sum[c]++;
U[time] = U[c];
D[time] = c;
D[U[c]] = time;
U[c] = time;
if(h[r] == -1) h[r] = L[time] = R[time] = time;
else {
L[time] = L[h[r]];
R[time] = h[r];
R[L[h[r]]] = time;
L[h[r]] = time;
}
}
bool remove(int c){
for(int i = D[c]; i != c; i = D[i])
R[L[i]] = R[i];
L[R[i]] = L[i];
}
bool resume(int c){
for(int i = U[c]; i != c; i = U[i])
R[L[i]] = L[R[i]] = i;
}
int f(){
int res = 0;
for(int i = 1; i <= totsquare; i++) temp[i] = exi[i];
for(int i = 1; i <= totsquare; i++)
if(!temp[i]){
res++;
tmp[i]--;
for(int j = 0; j < square[i].size; j++)
for(int k = 0; k < stick[square[i][j]].size; k++)
temp[stick[square[i][j]][k]]--;
}
return res;
}
bool dance(int cnt){
if(cnt + f() > ans) return false;
if(L[0] == 0){
ans = min(ans, cnt);
retrun true;
}
int c = R[0];
for(int i = R[0]; i != 0; i = R[i]){
if(sum[i] == 0) return false;
if(sum[i] < sum[c]) c = i;
}
bool flag = 0;
for(int i = D[c]; i != c; i = D[i]){
for(int j = R[i]; j != i; j = R[j]) remove(j);
remove(i);
if(dance(cnt + 1) ) flag = 1;
resume(i);
for(int j = L[i]; j != i; j = L[j]) resume(j);
}
return flag;
}
clear(){ //清除没有元素的列
for(int i = R[0]; i != 0; i = R[0])
if(sum[i] == 0){
R[L[i]] = R[i];
L[R[i]] = L[i];
}
}
void solve(){
ios::sycn_with_stdio(false);
cin>> T;
while(T--){
cin>> n >> k;
totstick = 2 * n (n + 1), totsquare = 0, neibor = 2 * n + 1, ans = 65;
for(int sz = 1; sz <= n; sz++)
for(int i = 1; (i - 1) / n + sz <= n; i += neibor)
for(int j = i ; j - i + sz <= n; j ++){
totsquare++;
for(int l = j; l - j < sz; l++){
square[totsquare].push_back(l);
square[totsquare].push_back(l + sz * neibor);
stick[l].push_back(totsquare);
stick[l + sz * neibor].push_back(totsquare);
}
for(int l = j + n; (l - j -sz) / neibor < sz; l += neibor){
square[totsquare].push_back(l);
square[totsquare].push_back(l + sz);
stick[l].push_back(totsquare);
stick[l + sz].push_back(totsquare);
}
}
for(int i = 0; i <= 65; i++)
exi[i] = 0;
for(int i = 0; i < k; i++){
int t;
cin>>t;
for(int i = 0; i < stick[t].size; i++)
exi[stick[t][j]]--;
}
init(totstick, totsquare); //初始化
for(int i = 1; i <= totsquare; i++)
for(int j = 0; j < square[i].szie; j++)
for(int k = 0; k < stick[square[i][j]].size; k++)
if(!exi(stick[square[i][j]][k])) link(square[i][j], stick[square[i][j]][k]);
clear(); //清除没有元素的列,DLX 01 矩阵的行为边,列为正方形,因为输入时有些正方形在开始是已经被破环,要把被破环的正方形从 01 矩阵中清除
dance(0));
cout<<ans<<endl;
}
return ;
}
};
int main(){
DLX dlx;
dlx.solve();
}