后面CSDN的做题记录就不再更新了(复制起来挺麻烦的)
以题解的形式发到CSDN里,cnblog里照常更新
P1129 [ZJOI2007] 矩阵游戏
题面:
题目描述
小 Q 是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏――矩阵游戏。矩阵游戏在一个 n × n n \times n n×n 黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:
- 行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)。
- 列交换操作:选择矩阵的任意两列,交换这两列(即交换对应格子的颜色)。
游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。
对于某些关卡,小 Q 百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!于是小 Q 决定写一个程序来判断这些关卡是否有解。
输入格式
本题单测试点内有多组数据。
第一行包含一个整数 T T T,表示数据的组数,对于每组数据,输入格式如下:
第一行为一个整数,代表方阵的大小 n n n。
接下来 n n n 行,每行 n n n 个非零即一的整数,代表该方阵。其中 0 0 0 表示白色, 1 1 1 表示黑色。输出格式
对于每组数据,输出一行一个字符串,若关卡有解则输出
Yes
,否则输出No
。样例 #1
样例输入 #1
2 2 0 0 0 1 3 0 0 1 0 1 0 1 0 0
样例输出 #1
No Yes
提示
数据规模与约定
- 对于 20 % 20\% 20% 的数据,保证 n ≤ 7 n \leq 7 n≤7。
- 对于 50 % 50\% 50% 的数据,保证 n ≤ 50 n \leq 50 n≤50。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 200 1 \leq n \leq 200 1≤n≤200, 1 ≤ T ≤ 20 1 \leq T \leq 20 1≤T≤20。
图匹配最难三连问:这道题在考匹配?如何匹配?图怎么建?
这道题很神奇的在考二分图匹配。
为什么?
我们将矩阵看做 i : ( 1 ∼ n ) ⇝ j : ( n + 1 ∼ n + n ) i:(1 \sim n) \leadsto j:(n + 1 \sim n + n) i:(1∼n)⇝j:(n+1∼n+n) 连边的邻接矩阵
其中 1 1 1 表示有连边, 0 0 0 表示未连边
那么,这交换两行/两列操作在干吗?
假设我们有这样的一个二分图
我们将这两列连边情况交换: a l e f t : [ 1 ∼ n ] ⇄ b l e f t : [ 1 ∼ n ] a_{left}:[1\sim n] \rightleftarrows b_{left}:[1\sim n] aleft:[1∼n]⇄bleft:[1∼n]
其实就是交换
a
l
e
f
t
、
b
l
e
f
t
a_{left}、b_{left}
aleft、bleft,(以交换
1
,
3
1,3
1,3 举例)如图
交换行也是一样的,即: a r i g h t ⇄ b r i g h t a_{right} \rightleftarrows b_{right} aright⇄bright
我们要让主对角线全为 1 1 1
也就是
1
⇝
n
+
1
2
⇝
n
+
2
⋮
n
⇝
n
+
n
\begin{align*} 1 \leadsto n + 1 \\ 2 \leadsto n + 2 \\ \vdots \\ n \leadsto n + n \\ \end{align*}
1⇝n+12⇝n+2⋮n⇝n+n
如图:
(? 代表我们不需要知道是否有连边)
也就是对应的左右边两点有连边,即:
这个时候就可以使用二分图完美匹配
也就是二分图最大匹配 + 二分图每个点都被匹配
因为这个是 n × n n \times n n×n 的矩阵,所以二分图中的左右两边的点肯定相等
(tips:非偶数个点的二分图无法完美匹配,必定有一个点无法匹配)
跑一个二分图匹配,判断是否有 n n n 条边匹配就大功告成了
AC-code:
#include<bits/stdc++.h>
using namespace std;
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
const int N = 205;
int match[N * N],n,vis[N * N];
int head[N * 2],nxt[N * N * 2],to[N * N * 2],cnt;
void add(int u,int v) {
nxt[cnt] = head[u];
to[cnt] = v;
head[u] = cnt++;
}
void solve() {
memset(head,-1,sizeof(head));
cnt = 0;
n = rd();
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
if(rd())
add(i,j + n),add(j + n,i);
int ans = 0;
memset(match,0,sizeof(match));
memset(vis,0,sizeof(vis));
auto dfs = [&](auto self,int x,int tag) -> bool{
for(int i = head[x];~i;i = nxt[i]) {
int y = to[i];
if(vis[y] == tag) continue;
vis[y] = tag;
if(!match[y] || self(self,match[y],tag)) {
match[y] = x;
return true;
}
}
return false;
};
for(int i = 1;i<=n;i++) ans += dfs(dfs,i,i);
if(ans == n) puts("Yes");
else puts("No");
}
signed main() {
int T = rd();
while(T--) solve();
return 0;
}