一名苦逼的OIer,想成为ACMer

Iowa_Battleship

BZOJ1059或洛谷1129 [ZJOI2007]矩阵游戏

BZOJ原题链接

洛谷原题链接

通过手算几组例子后,很容易发现,同一列的\(1\)永远在这一列,且这些\(1\)有且仅有一个能产生贡献,行同理。
所以我们可以只考虑交换列,使得每一行都能匹配一个\(1\),且每一行匹配的\(1\)没有重列的,最后交换行排序下即可达到目标。
解决这个问题就不难了,对于一个格子\((x,y)\),若为\(1\),则从它所在的行\(x\)向列\(y\)连一条边,跑一边二分图最大匹配,判断是否是完全匹配即可。

#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1010;
const int M = 1e5 + 10;
int fi[N], ne[M], di[M], mtc[N], l;
bool v[N];
inline int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c < '0' || c > '9'; c = getchar())
		p |= c == '-';
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + c - '0';
	return p ? -x : x;
}
inline void add(int x, int y)
{
	di[++l] = y;
	ne[l] = fi[x];
	fi[x] = l;
}
bool dfs(int x)
{
	int i, y;
	for (i = fi[x]; i; i = ne[i])
		if (!v[y = di[i]])
		{
			v[y] = 1;
			if (!mtc[y] || dfs(mtc[y]))
			{
				mtc[y] = x;
				return true;
			}
		}
	return false;
}
int main()
{
	int i, j, n, s, x, t;
	t = re();
	while (t--)
	{
		n = re();
		memset(mtc, 0, sizeof(mtc));
		memset(fi, 0, sizeof(fi));
		s = l = 0;
		for (i = 1; i <= n; i++)
			for (j = 1; j <= n; j++)
			{
				x = re();
				if (x)
					add(i, j + n);
			}
		for (i = 1; i <= n; i++)
		{
			memset(v, 0, sizeof(v));
			if (dfs(i))
				s++;
		}
		s ^ n ? printf("No\n") : printf("Yes\n");
	}
	return 0;
}

posted on 2018-10-17 13:45  Iowa_Battleship  阅读(91)  评论(0编辑  收藏  举报

导航