题意:每一行的某些列给定为1,现在问是否能找某些行,使得每一列的1出现一次
思路:与可重复覆盖的区别就是选定的列的1与其它行的又重复的,那么重复的那行也需要删除,而可重复的不需要删除,那么直接改一改之前的那个模版就可以求这道题了,而且需要输出所选行,任意一个就可以,那么在查找的时候直接赋给一个数组即可
#include <vector> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; const int inf=0x3f3f3f3f; const ll INF=0x3f3f3f3f3f3f3f3fll; const int maxn=1010; int L[maxn*100],R[maxn*100],U[maxn*100],D[maxn*100];//节点的上下左右四个方向的链表 int C[maxn*100],H[maxn],cnt[maxn],vis[maxn],ans[maxn],Row[maxn*100];//C列H行cnt列链表中元素个数 int n,m,id,len; void init(){ for(int i=0;i<=m;i++){ cnt[i]=0;U[i]=D[i]=i; L[i+1]=i;R[i]=i+1; } R[m]=0;id=m+1; memset(H,-1,sizeof(H)); } void Link(int r,int c){ cnt[c]++;C[id]=c;Row[id]=r; U[id]=U[c];D[U[c]]=id; D[id]=c;U[c]=id;Row[id]=r; if(H[r]==-1) H[r]=L[id]=R[id]=id; else{ L[id]=L[H[r]];R[L[H[r]]]=id; R[id]=H[r];L[H[r]]=id; } id++; } void Remove(int Size){ L[R[Size]]=L[Size]; R[L[Size]]=R[Size]; for(int i=D[Size];i!=Size;i=D[i]){ for(int j=R[i];j!=i;j=R[j]){ U[D[j]]=U[j];D[U[j]]=D[j]; cnt[C[j]]--; } } } void Resume(int Size){ for(int i=D[Size];i!=Size;i=D[i]){ for(int j=R[i];j!=i;j=R[j]){ U[D[j]]=j;D[U[j]]=j; cnt[C[j]]++; } } L[R[Size]]=Size;R[L[Size]]=Size; } int Dance(int k){ int pos,mm=maxn; if(R[0]==0){ len=k; return 1; } for(int i=R[0];i;i=R[i]){ if(mm>cnt[i]){ mm=cnt[i];pos=i; } } Remove(pos); for(int i=D[pos];i!=pos;i=D[i]){ ans[k]=Row[i]; for(int j=R[i];j!=i;j=R[j]) Remove(C[j]); if(Dance(k+1)) return 1; for(int j=L[i];j!=i;j=L[j]) Resume(C[j]); } Resume(pos); return 0; } int main(){ int u,v,a; while(scanf("%d%d",&n,&m)!=-1){ init(); for(int i=1;i<=n;i++){ scanf("%d",&a); while(a--){ scanf("%d",&u); Link(i,u); } } int flag=Dance(0); if(flag==0) printf("NO\n"); else{ printf("%d",len); for(int i=0;i<len;i++) printf(" %d",ans[i]); printf("\n"); } } return 0; }
时间: 2024-10-06 08:05:46