题目描述
传送门
题意:给出一个矩阵,每次可以选择反转行或列,求一种合法的方案使最终的矩阵所有行的权值和和列的权值和分别大于0。
题解
每次贪心地找负权最大的一行或一列并把其反转。直到没有负权的行或列。可以证明矩阵最大的权值和为所有权值的绝对值之和。
只有反转奇数次的行或列在最终的答案中出现。
刚开始做的有点问题,胡改八改就改成了奇丑无比的代码。
其实似乎不贪心地直接做也可以。TA说的。。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=105;
int n,m,a[max_n][max_n];
int now,k,Max; bool flag;
int A,B,line[1000],list[1000],sumline[max_n],sumlist[max_n],ans1[1000],ans2[1000],tot1,tot2,cnt;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
{
scanf("%d",&a[i][j]);
sumline[i]+=a[i][j];
sumlist[j]+=a[i][j];
}
while (1)
{
flag=false; Max=0;
for (int i=1;i<=n;++i)
{
now=sumline[i];
for (int j=1;j<=m;++j)
now=now-a[i][j]-a[i][j];
if (now-sumline[i]>0) flag=true;
if (now-sumline[i]>Max)
{
Max=now-sumline[i];
k=i;
}
}
for (int i=1;i<=m;++i)
{
now=sumlist[i];
for (int j=1;j<=n;++j) now=now-a[j][i]-a[j][i];
if (now-sumlist[i]>0) flag=true;
if (now-sumlist[i]>Max)
{
Max=now-sumlist[i];
k=n+i;
}
}
if (!flag) break;
if (k<=n)
{
line[++A]=k;
for (int i=1;i<=m;++i) a[k][i]=-a[k][i];
sumline[k]=0;
for (int i=1;i<=m;++i) sumline[k]+=a[k][i];
}
else
{
k-=n;
list[++B]=k;
for (int i=1;i<=n;++i) a[i][k]=-a[i][k];
sumlist[k]=0;
for (int i=1;i<=n;++i) sumlist[k]+=a[i][k];
}
}
sort(line+1,line+A+1);
cnt=0;
for (int i=1;i<=A;++i)
if (line[i]!=line[i-1])
{
if (cnt%2!=0&&i!=1) ans1[++tot1]=line[i-1];
cnt=1;
}
else cnt++;
if (cnt%2!=0) ans1[++tot1]=line[A];
sort(list+1,list+B+1);
cnt=0;
for (int i=1;i<=B;++i)
if (list[i]!=list[i-1])
{
if (cnt%2!=0&&i!=1) ans2[++tot2]=list[i-1];
cnt=1;
}
else cnt++;
if (cnt%2!=0) ans2[++tot2]=list[B];
if (tot1==0) printf("0\n");
else printf("%d ",tot1);
for (int i=1;i<=tot1;++i) printf("%d%c",ans1[i]," \n"[i==tot1]);
if (tot2==0) printf("0\n");
else printf("%d ",tot2);
for (int i=1;i<=tot2;++i) printf("%d%c",ans2[i]," \n"[i==tot2]);
}