=== ===
這裏放傳送門
=== ===
題解
一開始感覺沒什麼思路然後上來就開始手玩。。玩了一會兒以後發現它每次交換都會導致行和列相互影響這真是太噁心了。。但是也發現黑點之間行和列的相對關係是不會變的,這個意思是說如果這一行有兩個黑點,那麼肯定不能通過什麼變換把這兩個點拆到兩行去,列也是一樣的。
那這個題就變成能不能選出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;
}