链接:https://ac.nowcoder.com/acm/problem/200190
来源:牛客网
牛妹在玩一个名为矩阵消除的游戏,矩阵的大小是n{n}n行m{m}m列,第i{i}i行第j{j}j列的单元格的权值为ai,ja_{i,j}ai,j,牛妹可以进行k{k}k个回合的游戏,在每个回合,牛妹可以选择一行或者选择一列,然后将这一行或者这一列的所有单元格中的权值变为0{0}0,同时牛妹的分数会加上这一行或者这一列中的所有单元格的权值的和。
牛妹想最大化她的得分,球球你帮帮她吧!
输入描述:
第一行三个整数n,m,k{n,m,k}n,m,k 接下来n{n}n行每行m{m}m个整数表示矩阵中各个单元格的权值。
输出描述:
输出一个整数表示牛妹能获得的最大分数。
示例1
输入
3 3 2 101 1 102 1 202 1 100 8 100
输出
414
备注
1≤n,m≤15
1≤ai,j≤1e61\leq a_{i,j}\leq 1e61≤ai,j≤1e6
1≤k≤n∗m1\leq k\leq n*m1≤k≤n∗m
分析:
对于题意,即枚举每行每列的各种排列方式,贪心算出k范围内最大值。
因此,对于解法,可以先枚举行(也可以先枚举列),要注意的是枚举行的数目不是一成不变的,如可以是只枚举一行,也可以只枚举两行....。每得到一种行的枚举方式时,只需要在此基础上再算出每列的和,并按从大到小的顺序排序好每列的和,贪心从大到小且一定数量地与行和相加构成矩阵中的众多枚举方式其中一种方式的总和sum(即sum=行和+(max...)列和,而对于答案ans则在各种sum中找出最大值即可。
而对于枚举方式,则可以用二进制的01串枚举方式。
具体的代码实现如下:
#include<iostream>
#include<algorithm>
#include<string.h>
#define N 20
using namespace std;
int n,m,k,ans;
int a[N][N],b[N*N];
int sumh[N],suml[N];
bool cmp(int aa,int bb){
return aa > bb;
}
int query( int x ){
memset(b,0,sizeof b);
int cnt = 0, i = 1;
while( x ){
if( x&1 ){
b[i] = 1;///对于选择情况用b数组来存,1选0不选,不选就不用赋值
cnt++;
}
i++;;x>>=1;
}
return cnt;
}
int main(){
cin >> n >> m >> k;
if( k > min(n,m) )k = min(n,m);///如果k>n||k>m,那么另一种就不用枚举了
///如果没有该if语句 就会造成sum多加上另一部分
for( int i = 1; i <= n; i ++ ){
for( int j = 1; j <= m; j ++ ){
cin >> a[i][j];
sumh[i] += a[i][j];///预处理出每行的和,这样在下面的01串处理中就可以直接用上对应的行和sumh[i];
}
}
for( int i = 0; i < (1<<n) ; i ++ ){///用01串枚举每行是否选择
int cnth = query(i);///选择行的个数
int cntl = k-cnth;///选择列的个数
if( cntl < 0 || cntl > m )continue;///判断取值是否合法
int sum = 0;
for( int j = 1; j <= n; j ++ ){///枚举每一行判断是否选择,b[i]=1即选择,选择则用sum+=当前行的行和
if( b[j] ){
sum += sumh[j];
}
}
memset(suml,0,sizeof suml);///每次的列和都不一样 所以在算列和之前都要清零
for( int j = 1; j <= n; j ++ ){
for( int k = 1; k <= m; k ++ ){
if( !b[j] ){///b[i]=0即当前的行没有被选择,没有被选择就加到对应的列和上,
suml[k] += a[j][k];
}
}
}
sort(suml+1,suml+1+m,cmp);///从大到小排序,方便最后sum可以贪心得到最大值
for( int j = 1; j <= cntl; j ++ ){
sum += suml[j];///行和与列和相加贪心得到sum
}
ans = max(ans,sum);
}
printf("%d",ans);
return 0;
}