版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a54665sdgf/article/details/81584632
题目大意:告诉你一个矩阵每一行和每一列的元素和的奇偶性,让你找出一个满足要求的矩阵,要求矩阵上每个位置的数均为0或1,而且1的数量要尽可能多。并且在此基础上,矩阵的字典序要尽可能小。
样例输入1
0110
1001
样例输出1
1111
0111
1110
1111
样例输入2
0
1
样例输出2
-1
样例输入3
11
0110
样例输出3
1011
1101
这道题让我回想起了以前做过的UVA - 11082,两者的题意非常相近,唯一的区别就是把矩阵每行每列的和换成了异或和,于是就不由自主地想用网络流的方法做,把自己的思路带偏了......
其实这道题用贪心的方法就能过。
首先我们发现,改变矩阵的一个元素,导致的结果必然是它所对应的行和列的奇偶性同时发生变化。
于是我们可以先把矩阵的所有位置都填上1,然后算出有哪些行和列的奇偶性需要发生改变,再将相应的位置变为0即可。则问题转化成了:给你两个01串,你每次都能从两个串中各挑一个位置将这个位置上的0或1反转,让你用尽可能少的操作,把两个串的所有位置都变成0。
不难发现,当两个串中1的个数为奇数的时候,问题是无解的,因为你的操作不会改变1的个数的奇偶性。
考虑1的个数为偶数的情况:
1.当两个串中的1的个数相等时,可以按照它们所在行和列的下标两两配对。
2.当两个串中1的个数不相等时,可以先把行或列所对应的串中多余的1与下标最小的列或行进行配对,然后剩下的按照1中的方法处理。
注意为了使得操作后产生的字典序最小,每次操作总是要尽可能选择下标小的行和列进行配对。(贪心思想)
AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#define FRER() freopen("i.txt","r",stdin)
#define FREW() freopen("o.txt","w",stdout)
using namespace std;
typedef long long LL;
const int N=50+10;
int a[N][N],n,m;
string r,c;
bool solve()
{
n=r.length(),m=c.length();
//将矩阵中的所有元素初始化为1
for(int i=0; i<n; ++i)
for(int j=0; j<m; ++j)
a[i][j]=1;
//算出需要改奇偶性的行和列,1代表需要改变。如果行或者列为奇数,则需要反转1和0
if(m&1)
{
for(int i=0; i<n; ++i)
r[i]=r[i]=='0'?'1':'0';
}
if(n&1)
{
for(int i=0; i<m; ++i)
c[i]=c[i]=='0'?'1':'0';
}
//统计r和c中1的个数,如果和为奇数则无解
int cntr=count(r.begin(),r.end(),'1');
int cntc=count(c.begin(),c.end(),'1');
if((cntr+cntc)&1)return false;
//将行或者列中多余的1与第一列或者第一行配对
for(int i=0; cntr>cntc; ++i)
{
if(r[i]=='1')
{
r[i]='0';
a[i][0]=0;
cntr--;
}
}
for(int i=0; cntc>cntr; ++i)
{
if(c[i]=='1')
{
c[i]='0';
a[0][i]=0;
cntc--;
}
}
//剩下的行和列一一配对
for(int i=0,j=0; i<n; ++i)
{
if(r[i]=='1')
{
while(c[j]=='0')++j;
a[i][j]=0;
++j;
}
}
return true;
}
int main()
{
//FRER();
cin>>r>>c;
if(solve())
{
for(int i=0; i<n; ++i)
{
for(int j=0; j<m; ++j)
printf("%d",a[i][j]);
printf("\n");
}
}
else
{
printf("-1\n");
}
return 0;
}