【BZOJ3816】【清华集训2014】矩阵变换 稳定婚姻问题

题目描述

  给出一个(n)(m)列的矩阵(A), 保证满足以下性质:

   1.(m>n)

   2.矩阵中每个数都是([0,n])中的自然数。

   3.每行中,([1,n])中每个自然数都恰好出现一次。这意味着每行中(0)恰好出现(m−n)次。

   4. 每列中,([1,n])中每个自然数至多出现一次。

现在我们要在每行中选取一个非零数,并把这个数之后的数赋值为这个数。我们希望保持上面的性质4,即每列中,([1,n])中每个自然数仍然至多出现一次。

  (nleq 200,mleq 400)

题解

  建立稳定婚姻问题的模型。

  把行看成男士,把数看成女士。每行喜欢出现靠左的数,每个数喜欢出现位置(这个数在这行中出现的位置)靠右的行。

  为什么这样一定合法?

  假设有这样的情况:

[x:uuuuuuuuuuu\ y:~~~~~u~~~~~~~~vvvvv ]

  那么在第(y)行中,(y)会更喜欢(u)(因为比(v)出现位置靠左)。(u)也会更喜欢(y)(因为出现位置靠右),那么就不是一个合法解。

  其实比读入还快。

  时间复杂度:(O(nm))

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<queue>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
void rd(int &s)
{
	int c;
	while((c=getchar())<'0'||c>'9');
	s=c-'0';
	while((c=getchar())>='0'&&c<='9')
		s=s*10+c-'0';
}
int a[210][410];
int n,m;
int b[210][210];
int now[210];
int c[210][210];
int d[210];
queue<int> q;
void solve()
{
//	scanf("%d%d",&n,&m);
	rd(n);
	rd(m);
	int i,j;
	memset(now,0,sizeof now);
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		{
//			scanf("%d",&a[i][j]);
			rd(a[i][j]);
			if(a[i][j])
			{
				b[i][++now[i]]=a[i][j];
				c[a[i][j]][i]=j;
			}
		}
	for(i=1;i<=n;i++)
	{
		q.push(i);
		now[i]=1;
	}
	memset(d,0,sizeof d);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		int v=b[x][now[x]];
		if(d[v]&&c[v][x]<c[v][d[v]])
		{
			now[x]++;
			q.push(x);
		}
		else
		{
			if(d[v])
				q.push(d[v]);
			d[v]=x;
		}
	}
	for(i=1;i<=n;i++)
		printf("%d ",b[i][now[i]]);
	printf("
");
}
int main()
{
	freopen("d2t3.in","r",stdin);
	freopen("d2t3.out","w",stdout);
	int t;
//	scanf("%d",&t);
	rd(t);
	while(t--)
		solve();
	return 0;
}
原文地址:https://www.cnblogs.com/ywwyww/p/8513151.html