1398. 烤饼干
题目描述
NOIP烤饼干时两面都要烤,而且一次可以烤R(1<=R<=10)行C(1<=C<=10000)列个饼干,当一面烤到规定时间时,机器会把整个翻过来以接着烤另一面。
有一天,正当机器准备翻饼干时发生了地震,有一些饼干被翻了过来,有一些没有。幸运的是,地震过后你可以手工操作,一次可以同时翻若干行或者若干列,但不能单独翻某一个饼干。
写一个程序计算通过翻转使得最终翻过来的饼干的数量得最大值。 例如下图是地震之后的情况,黑点表示未翻转,白点表示已经翻转:
翻转第一行后得到:
接着翻转第1列和第5列得到下图:
这样可以使得9个饼干翻转过来。
输入
第1行: 两个整数R和C(1<=R<=10,1<=C<=10000);
接下来R行,每行C个空格隔开的数,其中aij=1表示未被翻转,0表示已经翻转。
输出
输出一个整数表示通过翻转行列操作最多被翻转的饼干数量。
样例输入
样例输入1:
2 5
0 1 0 1 0
1 0 0 0 1
样例输入2:
3 6
1 0 0 0 1 0
1 1 1 0 1 0
1 0 1 1 0 1
样例输出
样例输出1:
9
样例输出2:
15
方法一(dfs):
因为最多只有十行,所以行的变化只有210=1024种。我们有深搜去模拟每一种行的变化。
对于每一列,我们使用贪心,只有翻后已翻过的干更多,我们才翻。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e6;
int n,m,a[11][10010],ans;
void dfs(int step)
{
if(step>n)
{
int temp=0;
for(int j=1;j<=m;j++)
{
int t=0;
for(int i=1;i<=n;i++)
t+=a[i][j];
temp+=max(t,n-t);
}
ans=max(ans,temp);
return ;
}
dfs(step+1);
for(int i=1;i<=m;i++) a[step][i]^=1;
dfs(step+1);
for(int i=1;i<=m;i++) a[step][i]^=1;
}
int main()
{
//fre();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
dfs(1);
printf("%d",ans);
return 0;
}
方法二(模拟):
我们用两层循环来进行模拟,外循环来枚举行的所有变化(1<<n种),内循环统计该种变化下能翻的最多饼干数,跟上面贪心一致,但用了状压dp的思想和一些套路。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e6;
int data,n,m,f[N];
long long tot,ans;
int sum(int x)
{
int i=0;
for(i=0;x;i++) x&=(x-1);
return i;
}
int main()
{
//fre();
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>data;
f[j]=(f[j]<<1)|data;
}
for(int i=0;i<(1<<n);i++)
{
tot=0;
for(int j=1;j<=m;j++)
{
int t=sum(i^f[j]);
tot+=max(t,n-t);
}
ans=max(ans,tot);
}
cout<<ans;
return 0;
}
方法三(状压dp):
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e6;
int n,m,data,f[2010],ans,a[10010];
int main()
{
//fre();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&data),a[j]=(a[j]<<1)|data;
for(int i=1;i<=m;i++)
for(int j=0;j<(1<<n);j++)
{
int tot=0;
for(int k=0;k<n;k++)
if(1<<k&j) {if(1<<k&a[i]) tot++;}
//i列第k行要翻,且第k行的饼干未翻。
else if(!(1<<k&a[i])) tot++;
//i列第k行不翻,且第k行的饼干已翻。
f[j]+=max(tot,n-tot);
}
for(int i=0;i<(1<<n);i++) ans=max(ans,f[i]);
printf("%d",ans);
return 0;
}