题目描述
给你一个N*M 的矩阵,矩阵里面的元素要么是正数,要么是负数,它们的绝对值不大
于10000。现在你可以对矩阵进行两种操作:
1、将某一列的元素全部取反。
2、将某一行的元素全部取反。
你可以执行任意次操作。
Task:通过以上两种操作如果可以将整个矩阵的元素全变为正数的话,则输出最少的操
作次数,否则输出“impossible”(不包括双引号)。
题目分析
我们想一下,其实每行和每列最多只取反一次,因为取两次反等于不变。
所以我们可以用贪心的思想来做这一道题。
我们枚举每一行,看一看现在的正数和负数哪一个更多,如果负数比较少那当然把负数所在的每一列全部取反是最好的。否则,先把当前行取反然后再把取反后的负数所在列全部取反就是最好的。
但有两个特殊情况
1.因为每一行每一列都是至多取反一次,所以如果我们发现某一列取反了两次我们就要输出“impossible”。
2.如果我们最后的ans是大于等于
n∗m/2
,那么我们就要输出
n∗m−ans
,因为我们可以完全取反。
代码
#include<cstdio>
#include<cstring>
using namespace std;
int a[1100][1100];
bool bk[1100];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
}
}
int ans=0;bool bz=true;
memset(bk,true,sizeof(bk));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(bk[j]==false)
{
a[i][j]=-a[i][j];
}
}
int sum=0;
for(int j=1;j<=m;j++)
{
if(a[i][j]<0)
{
sum++;
}
}
if(sum<=m/2)
{
for(int j=1;j<=m;j++)
{
if(a[i][j]<0&&bk[j]==false)
{
bz=false;
break;
}
}
if(bz==false) break;
for(int j=1;j<=m;j++)
{
if(a[i][j]<0)
{
bk[j]=false;
}
}
ans+=sum;
}
else
{
for(int j=1;j<=m;j++)
{
if(a[i][j]>0&&bk[j]==false)
{
bz=false;
break;
}
}
if(bz==false) break;
for(int j=1;j<=m;j++)
{
if(a[i][j]>0)
{
bk[j]=false;
}
}
ans+=(m-sum)+1;
}
}
if(bz==true)
{
if(ans*2<=n+m) printf("%d\n",ans);
else printf("%d\n",n+m-ans);
}
else
{
printf("impossible\n");
}
return 0;
}