分析:看到这题呢,首先想到的就是搜索,数据范围也不大嘛。但是仔细思考发现这题用搜索很难做,看了大佬们的题解后学到了,这一类题目要用二分图匹配来做。可以知道,如果想要的话,每一个子都可以移动到任意位置(当然会对其他子造成影响),我们还可以发现,每一行或每一列有哪些子其实绝对是固定的,只是它们之间的相对顺序会改变。
瞎扯了这么多,进入正题吧,首先如果a[i][j]是黑子,那么我们想要得到的目标当然是把它移动到a[i][i],那么他当前的位置就可以看作j,目标位置是i,那么就把j和i之间连一条边,将所有边连接以后发现这其实是个二分图,那么就二分图匹配啊,如果能满足有n个匹配那么就说明目标状态可以达到(这个应该不难想吧)。所以建边以后直接匈牙利算法就OK了。(PS:补充一句,建边的时候别忘了把数组开大点,一开始脑抽忘了,结果RE得非常感人......)
Code:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<iomanip> #include<algorithm> const int N=201; int T,n; int a[N][N],match[N]; int head[N],size; bool vis[N]; struct Node{ int to,next; }edge[50005]; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} return flag?-num:num; } inline void ready() { size=0; memset(head,-1,sizeof(head)); memset(match,-1,sizeof(match)); memset(vis,false,sizeof(vis)); } inline void add(int x,int y) { edge[++size].to=y; edge[size].next=head[x]; head[x]=size; } inline bool dfs(int x) { for(int i=head[x];i!=-1;i=edge[i].next){ int y=edge[i].to; if(vis[y])continue; vis[y]=true; if(match[y]==-1||dfs(match[y])){ match[y]=x; return true;}} return false; } inline void work() { n=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i][j])add(j,i); for(int i=1;i<=n;i++){ memset(vis,false,sizeof(vis)); if(!dfs(i)){printf("No\n");return;}} printf("Yes\n"); } int main() { T=read(); while(T--){ ready(); work();} return 0; }