题意:n*n的矩形阵(n<=5),由2*n*(n+1)根火柴构成,那么其中会有很多诸如边长为1,为2...为n的正方形,现在可以拿走一些火柴,那么就会有一些正方形被破坏掉。问,在已经拿走一些火柴的情况下,还需要拿走至少多少根就可以把所有的正方形破坏掉。
题解:可以用dancing links做,让火柴做为行,让所有的正方形作为列,且如果i火柴能让j正方形破坏掉,就让第i行第j列为1,然后做一次可重复的覆盖,取最小值便可以得到答案。另外,涉及两个优化,
1、最优化剪枝,即最好情况下也不会比当前最优值更优的剪枝。
2、不必一开始就将所有的火柴棍与正方形的对应关系加入到DLX中,应该在读完所有数据之后,判断哪些正方形已经被删除了(即该列无效),只加入有效的结点。
View Code
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int inf=1<<30; 6 const int NUM=100*60; 7 int cnt,L[NUM],R[NUM],S[NUM],D[NUM],U[NUM],C[NUM],O[NUM],H[NUM],X[NUM]; 8 /* 9 NUM:最大结点数 10 U,D,L,R:上下左右结点 11 C:列的头指针位置 12 O:储存答案 13 X:与O配合代表第几行(X[O[i]]]) 14 通过link(r,c)加点,dfs(0)运算 15 */ 16 void remove(int c) 17 { 18 for(int i=D[c];i!=c;i=D[i]) 19 { 20 L[R[i]]=L[i]; 21 R[L[i]]=R[i]; 22 } 23 } 24 void resume(int c) 25 { 26 for(int i=D[c];i!=c;i=D[i]) 27 { 28 L[R[i]]=i; 29 R[L[i]]=i; 30 } 31 } 32 int geth() 33 { 34 bool has[80]; 35 memset(has, false, sizeof(has)); 36 int res=0; 37 for(int i=R[0]; i!=0; i=R[i]) 38 if(!has[i]) 39 { 40 res++; 41 for(int j=D[i]; j!=i; j=D[j]) 42 for(int k=R[j]; k!=j; k=R[k]) 43 has[C[k]]=true; 44 } 45 return res; 46 } 47 int ans; 48 void dfs(int k) 49 { 50 if(!R[0]) 51 { 52 ans=min(k,ans); 53 return; 54 } 55 else if(k+geth()>=ans) 56 return; 57 int c=R[0]; 58 for(int t=R[0],ms=inf; t!=0; t=R[t]) 59 if(S[t]<ms) 60 ms=S[t],c=t; 61 for(int i=D[c];i!=c;i=D[i]) 62 { 63 remove(i); 64 for(int j=R[i]; j!=i; j=R[j]) 65 { 66 remove(j); 67 S[C[j]]--; 68 } 69 dfs(k+1); 70 for(int j=L[i]; j!=i; j=L[j]) 71 { 72 resume(j); 73 S[C[j]]++; 74 } 75 resume(i); 76 } 77 } 78 void build(int r,int c) 79 { 80 for(int i=0;i<=c;i++) 81 { 82 U[i]=D[i]=i; 83 L[i+1]=i; 84 R[i]=i+1; 85 C[i]=i; 86 S[i]=0; 87 } 88 R[cnt=c]=0; 89 while(r) 90 H[r--]=-1; 91 } 92 void link(int r,int c) 93 { 94 ++S[C[++cnt]=c]; 95 X[cnt]=r; 96 D[cnt]=D[c]; 97 U[D[c]]=cnt; 98 U[cnt]=c; 99 D[c]=cnt; 100 if(H[r]<0) 101 H[r]=L[cnt]=R[cnt]=cnt; 102 else 103 { 104 R[cnt]=R[H[r]]; 105 L[R[H[r]]]=cnt; 106 L[cnt]=H[r]; 107 R[H[r]]=cnt; 108 } 109 } 110 bool mark[80][80]; 111 void init(int n) 112 { 113 memset(mark,false,sizeof(mark)); 114 int i,j,k,si,num=0,c=1; 115 for(si=1;si<=n;si++) 116 { 117 for(i=1;i<=n-si+1;i++) 118 { 119 for(j=1;j<=n-si+1;j++) 120 { 121 for(k=0;k<si;k++) 122 { 123 mark[(i-1)*(2*n+1)+j+k][c]=true; 124 mark[(i-1+si)*(2*n+1)+j+k][c]=true; 125 mark[i*n+(i-1)*(n+1)+j+k*(2*n+1)][c]=true; 126 mark[i*n+(i-1)*(n+1)+j+k*(2*n+1)+si][c]=true; 127 } 128 c++; 129 } 130 } 131 } 132 } 133 int main() 134 { 135 int T,n; 136 for(scanf("%d",&T);T;T--) 137 { 138 scanf("%d",&n); 139 int num,row=2*n*(n+1),col=0; 140 for(int i=1;i<=n;i++) 141 col+=i*i; 142 build(row,col); 143 init(n); 144 scanf("%d",&num); 145 bool vis[80]; 146 memset(vis,false,sizeof(vis)); 147 for(int i=0;i<num;i++) 148 { 149 int r; 150 scanf("%d",&r); 151 for(int j=1;j<=col;j++) 152 { 153 if(mark[r][j]) 154 { 155 if(!vis[j]) 156 { 157 vis[j]=true; 158 R[L[j]]=R[j]; 159 L[R[j]]=L[j]; 160 R[j]=L[j]=0; 161 } 162 } 163 } 164 } 165 for(int i=1;i<=row;i++) 166 for(int j=1;j<=col;j++) 167 if(mark[i][j]&&!vis[j]) 168 link(i,j); 169 ans=100000; 170 dfs(0); 171 printf("%d\n",ans); 172 } 173 return 0; 174 }