=== ===
这里放传送门
=== ===
题解
一开始感觉没什么思路然后上来就开始手玩。。玩了一会儿以后发现它每次交换都会导致行和列相互影响这真是太恶心了。。但是也发现黑点之间行和列的相对关系是不会变的,这个意思是说如果这一行有两个黑点,那么肯定不能通过什么变换把这两个点拆到两行去,列也是一样的。
那这个题就变成能不能选出n个点来,让这两个点既不在同一行也不在同一列了。这就是比较常见的模型了,把行列分别放两排点,如果有一个黑点就从对应的行向对应的列连边,那么这就是一个二分图,二分图里的每一条边代表了一个黑点。
因为每行每列只能选一个黑点,那这就是一个二分图的匹配问题。如果最大匹配规模达不到n的话肯定就是无解了。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,p[410],a[100010],nxt[100010],link[410],vis[410],tot,cnt;
void add(int x,int y){
tot++;a[tot]=y;nxt[tot]=p[x];p[x]=tot;
}
bool find(int u,int k){
for (int i=p[u];i!=0;i=nxt[i])
if (vis[a[i]]!=k){
vis[a[i]]=k;
if (link[a[i]]==-1||find(link[a[i]],k)){
link[a[i]]=u;return true;
}
}
return false;
}
int main()
{
scanf("%d",&T);
for (int wer=1;wer<=T;wer++){
scanf("%d",&n);tot=cnt=0;
memset(p,0,sizeof(p));
memset(link,-1,sizeof(link));
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
int c;scanf("%d",&c);
if (c==1){add(i,j+n);add(j+n,i);}
}
for (int i=1;i<=2*n;i++){
vis[i]=i;
if (find(i,i)) ++cnt;
}
if (cnt==2*n) printf("Yes\n");
else printf("No\n");
}
return 0;
}