1. 程式人生 > 實用技巧 >P1129 [ZJOI2007]矩陣遊戲

P1129 [ZJOI2007]矩陣遊戲

Link

題目描述

小 Q 是一個非常聰明的孩子,除了國際象棋,他還很喜歡玩一個電腦益智遊戲――矩陣遊戲。矩陣遊戲在一個 \(n \times n\) 黑白方陣進行(如同國際象棋一般,只是顏色是隨意的)。每次可以對該矩陣進行兩種操作:

  • 行交換操作:選擇矩陣的任意兩行,交換這兩行(即交換對應格子的顏色)。
  • 列交換操作:選擇矩陣的任意兩列,交換這兩列(即交換對應格子的顏色)。

遊戲的目標,即通過若干次操作,使得方陣的主對角線(左上角到右下角的連線)上的格子均為黑色。

對於某些關卡,小 Q 百思不得其解,以致他開始懷疑這些關卡是不是根本就是無解的!於是小 Q 決定寫一個程式來判斷這些關卡是否有解。

輸入格式

本題單測試點內有多組資料

第一行包含一個整數 \(T\),表示資料的組數,對於每組資料,輸入格式如下:

第一行為一個整數,代表方陣的大小 \(n\)。 接下來 \(n\) 行,每行 \(n\) 個非零即一的整數,代表該方陣。其中 \(0\) 表示白色,\(1\) 表示黑色。

輸出格式

對於每組資料,輸出一行一個字串,若關卡有解則輸出 Yes,否則輸出 No

輸入輸出樣例

輸入 #1

2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0

輸出 #1

No
Yes

說明/提示

資料規模與約定

  • 對於 20% 的資料,保證 \(n \leq 7\)

  • 對於 50% 的資料,保證 \(n \leq 50\)

  • 對於 100% 的資料,保證 \(1 \leq n \leq 200\)\(1 \leq T \leq 20\)

題解:

自己匈牙利演算法寫炸了,調了好半天,原地退役。

看到資料範圍比較小,我們可能會想到是網路流的毒瘤題或者是二分圖匹配。

我們主要考慮怎麼建邊。

題目中要求的最終的狀態就是 對角線都是黑色的,也就是對於每一行至少要有一列且每一列不能重複,與他對應的格子為黑色。

那麼我們可以把每一行放在左邊,每一列放在右邊,對於每一個為黑色的格子將他的行與列連邊。

然後跑一邊匈牙利或者 \(Dinic\) 演算法的模板就可以。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m,tot,flag,x,T;
int head[510],match[510];
bool vis[510];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
struct node
{
	int to,net;
}e[100100];
void add(int x,int y)
{
	e[++tot].to = y;
	e[tot].net = head[x];
	head[x] = tot;
}
bool dfs(int x)
{
	for(int i = head[x]; i; i = e[i].net)
	{
		int to = e[i].to;
		if (!vis[to])
		{
			vis[to] = 1;
			if(!match[to] || dfs(match[to]))
			{
				match[to] = x;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	T = read();
	while(T--)
	{
		n = read(); tot = 0;
		memset(head,0,sizeof(head));
		memset(match,0,sizeof(match));
		for(int i = 1; i <= n; i++)
		{
			for(int j = 1; j <= n; j++)
			{
				x = read();
				if(x == 1) add(i,j+n);//如果這一個格子為黑色就把他的列與行連邊
			}
		}
		flag = 0;
		for(int i = 1; i <= n; i++)//二分圖最大匹配
		{
			memset(vis,0,sizeof(vis));
			if(!dfs(i)) 
			{
				flag = 1;
				break;
			}
		}
		if(flag == 1) printf("No\n");
		else printf("Yes\n");
	}
	return 0;
}