Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 328 Accepted Submission(s): 158
33
问题描述:选定最少的行,使得每列至少有一个1(即不一定为一个)
实现思路:将当前列去掉,并将选作当前列的解的行能够覆盖到的列全部去掉,因为不要求每列仅有一个1,故不必要把能够覆盖某一列的所有行全部去掉。remove和resume和精确覆盖有所不同。需要注意。
此外,由于删去的少了,所以矩阵密度的下降也变慢了,因此要加个A*剪枝来提高效率。就是利用A*搜索中的估价函数,即对于当前的递归深度K下的矩阵,估计其最好情况下即最少还需要多少步才能出解。即如果将能够覆盖当前列的所有行全部选中,去掉这些行能够覆盖到的列,将这个操作作为一层深度,重复次操作直到所有列全部出解的深度是多少。如果当前深度加上最佳步数已经不可能由于当前最优解,则直接返回不必再算这种情况。感觉和分支限界很像,其实以前也不自觉地经常用。
//转载自 http://blog.sina.com.cn/s/blog_6a46cc3f0100s2d4.html#include<cstdio> #define N 205 #define M 205 int m,n,cnt,size[M],ans,dir[4][2]={0,1,0,-1,1,0,-1,0}; struct Node { int r,c; Node *U,*D,*L,*R; }node[505],row[N],col[M],head; void init(int r,int c) { cnt=0; head.r=r;head.c=c; head.L=head.R=head.U=head.D=&head; for(int i=0;i<c;i++) { col[i].r=r;col[i].c=i; col[i].L=&head;col[i].R=head.R; col[i].U=col[i].D=col[i].L->R=col[i].R->L=&col[i]; size[i]=0; } for(int i=r-1;i>=0;i--) { row[i].r=i;row[i].c=c; row[i].U=&head;row[i].D=head.D; row[i].L=row[i].R=row[i].U->D=row[i].D->U=&row[i]; } } void insert(int r,int c) { Node *p=&node[cnt++]; p->r=r;p->c=c; p->R=&row[r];p->L=row[r].L; p->L->R=p->R->L=p; p->U=&col[c];p->D=col[c].D; p->U->D=p->D->U=p; size[c]++; } inline void cover(Node *p) { for(Node *C=p->D;C!=p;C=C->D) { size[C->c]--; C->L->R=C->R; C->R->L=C->L; } } inline void resume(Node *p) { for(Node *C=p->U;C!=p;C=C->U) { size[C->c]++; C->L->R=C->R->L=C; } } int h() { bool f[M]={0}; int k=0; Node *p,*R,*C; for(p=head.L;p!=&head;p=p->L) if(!f[p->c]) { k++; f[p->c]=1; for(C=p->U;C!=p;C=C->U) for(R=C->R;R!=C;R=R->R) f[R->c]=1; } return k; } void dfs(int k) { if(k+h()>=ans) return; if(head.L==&head) { if(k<ans) ans=k; return; } int INF=-1u>>1,c=-1; Node *p,*q; for(p=head.L;p!=&head;p=p->L) if(size[p->c]<INF) INF=size[c=p->c]; if(!INF) return; for(p=col[c].D;p!=&col[c];p=p->D) { cover(p); for(q=p->L;q!=p;q=q->L) cover(q); dfs(k+1); for(q=p->R;q!=p;q=q->R) resume(q); resume(p); } } int main() { while(~scanf("%d%d",&n,&m)) { int i,j,k,x,y,n1=0,n2=0,h1[255]={0},h2[255]={0}; char map[25][25]; for(i=0;i<n;i++) { scanf("%s",map[i]); for(j=0;j<m;j++) { if(map[i][j]=='.') h1[i*m+j]=n1++; if(map[i][j]=='#') h2[i*m+j]=n2++; } } init(n1,n2); for(i=0;i<n;i++) for(j=0;j<m;j++) if(map[i][j]=='.') for(k=0;k<4;k++) { x=i+dir[k][0]; y=j+dir[k][1]; while(map[x][y]=='.') { x+=dir[k][0]; y+=dir[k][1]; } if(map[x][y]=='#') insert(h1[i*m+j],h2[x*m+y]); } for(i=0;i<n1;i++) { row[i].R->L=row[i].L; row[i].L->R=row[i].R; } ans=999; dfs(0); printf("%d\n",ans); } }