《算法竞赛进阶指南》0x28IDA* POJ1084 Square Destoryer
题目链接:https://www.acwing.com/problem/content/184/
给出正方形和某些边被删除,要求删除最少的边数使得其中没有任何正方形,策略就是将所有的正方形都存起来,可以从图中看见正方形的四条边都是等差数列,边长为5的大正方形拥有60条小边
接下来只要枚举最小的正方形,并且删除每条边,向下一个状态转移,如果删除所有的边都无法在指定的深度达到目标状态,就return false,return true的条件是没有完整的正方形,
在此我们需要用IDA*估计未来至少需要的步数f(),计算方式是看有多少个完整的独立的正方形,每个独立的正方形一定至少删除一条边,所以未来的代价一定是大于等于我们估计的代价的,满足估价函数
的性质,估价函数在计算中需要将完整边数的正方形的所有边都删除,需要使用一个新的bool数组保存边原来的情况,计算完成后再复制回去。
注意点就是dfs中的return条件
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define maxn 80 int n,m; bool st[maxn];//标记编号是否拿走 vector<int> square[maxn];//保存每个正方形的边长,等差数列 bool state[maxn]; bool check(vector<int> &sq){ for(int i=0;i<sq.size();i++) if(st[sq[i]]) return false; return true; } int f(){//计算当前有多少个完整的正方形 memcpy(state,st,sizeof(st)); int ret=0; for(int i=0;i<m;i++){ vector<int> &sq = square[i]; if(check(sq)){ ret++; for(int j=0;j<sq.size();j++){//将该正方形的所有边都删除 st[sq[j]]=true; } } } memcpy(st,state,sizeof(state)); return ret; } bool dfs(int depth,int max_depth){ if(depth+f() > max_depth)return false; for(int i=0;i<m;i++){ vector<int> &sq = square[i]; if(check(sq)){//最小的完整的正方形 for(int j=0;j<sq.size();j++){ int x=sq[j]; st[x]=true; if(dfs(depth+1,max_depth))return true; st[x]=false; } return false;//最小的正方形删边之后无法成功搜索 } } return true;//没有完整的正方形 } int main(){ int T; cin>>T; while(T--){ scanf("%d",&n); memset(st,0,sizeof(st)); m=0; for(int len=1;len<=n;len++)//枚举边长 for(int a=1;a+len-1<=n;a++)//枚举左上角的起点 for(int b=1;b+len-1<=n;b++) { square[m].clear(); int d=2*n+1;//保存公差 for(int i=0;i<len;i++){//插入4*len条边 square[m].push_back(1+(a-1)*d+b-1+i);//上边,等差数列 square[m].push_back(1+(a+len-1)*d+b-1+i);//下边 square[m].push_back(n+1+(a-1)*d+b-1+i*d);//左边 square[m].push_back(n+1+(a-1)*d+b-1+i*d+len);//右边 } m++; } int k=0; scanf("%d",&k); while(k--){ int x; scanf("%d",&x); st[x]=true; } int depth=0; while(!dfs(0,depth))depth++; printf("%d\n",depth); } }
每一个不曾起舞的日子,都是对生命的辜负。