矩阵游戏 bzoj-1059 ZJOI-2007
题目大意:给定一个n*n的棋盘,上面有一些格子被染黑,剩下都是白色。你每次可以交换两列或者两行,问你能否通过一系列操作使得棋盘的主对角线上的格子全是黑色。
注释:$1le nle 200$。
想法:
我们发现一个小性质,就是两个格子如果同行那么无论如何操作他们都同行。如果同列那么无论任何时刻他们都同列。
这样的话我们就是要找到每行每列都需要有黑格子。
我们将行和列抽象成点直接跑匈牙利判断一下最大匹配是不是n即可。
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 210 using namespace std; int fr[N<<1]; bool line[N][N<<1],used[N<<1]; int a[N][N]; int n; inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} int rd() {int x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;} bool find(int x) { for(int i=n+1;i<=n<<1;i++) if(line[x][i]&&!used[i]) { used[i]=true; if(!fr[i]||find(fr[i])) {fr[i]=x; return true;} } return false; } void init() { memset(fr,0,sizeof fr); memset(used,false,sizeof used); memset(line,false,sizeof line); } bool work() { for(int i=1;i<=n;i++) { memset(used,false,sizeof used); if(!find(i)) return false; } return true; } int main() { int cases=rd(); while(cases--) { init(); n=rd(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { a[i][j]=rd(); if(a[i][j]) line[i][j+n]=true; } printf("%s ",work()?"Yes":"No"); } return 0; }
小结:二分图真的是好东西啊qwq。